PHPackages                             lexide/lazy-boy - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. lexide/lazy-boy

ActiveLibrary[API Development](/categories/api)

lexide/lazy-boy
===============

A skeleton REST API application, using Slim and Syringe with support for Symfony Console, AWS Lambda and ApiGateway

v4.3.2(3mo ago)03.1k↓33.3%MITPHPPHP &gt;=8.0.0CI failing

Since Feb 2Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/lexide/lazy-boy)[ Packagist](https://packagist.org/packages/lexide/lazy-boy)[ Docs](https://github.com/lexide/lazy-boy)[ RSS](/packages/lexide-lazy-boy/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (8)Versions (56)Used By (0)

LazyBoy
=======

[](#lazyboy)

A skeleton REST API application, using [Slim](https://github.com/slimphp/slim), [Syringe](https://github.com/Lexide/syringe) and [Puzzle-DI](https://github.com/lexide/puzzle-di)

Summary
-------

[](#summary)

LazyBoy will create a skeleton [Slim](https://github.com/slimphp/slim) framework, using [ProForma](https://github.com/lexide/pro-forma) to generate files, so you can create REST APIs without having to bother with boilerplate code.

It is packaged with a route loader and uses [Syringe](https://github.com/Lexide/syringe), which allows you to define both your routes and services in configuration files, rather than PHP

If you have the [Symfony console](https://github.com/symfony/console) installed, it will also create a console script including and command services that have been defined in [Syringe](https://github.com/Lexide/syringe) DI config. You can also use [Puzzle-DI](https://github.com/lexide/puzzle-di) to load service configuration from modules.

Requirements
------------

[](#requirements)

- PHP 8.0+
- [Slim](https://github.com/slimphp/slim) 4.0+
- [Syringe](https://github.com/Lexide/syringe) 2.2+

Installation
------------

[](#installation)

Install using composer:

```
composer require lexide/lazy-boy:~4.0.0

```

Installation will include [Puzzle-DI](https://github.com/lexide/puzzle-di) and [ProForma](https://github.com/lexide/pro-forma) as dependencies, which have plugins that LazyBoy requires, so you will need to enable those when asked.

You will also need to whitelist LazyBoy for use with ProForma in Puzzle-DI. Both steps can be done by adding the following config to `composer.json`:

```
{
  "config": {
    "allow-plugins": {
      "lexide/pro-forma": true,
      "lexide/puzzle-di": true
    }
  },
  "extra": {
    "lexide/puzzle-di": {
      "whitelist": {
        "lexide/pro-forma": [
          "lexide/lazy-boy"
        ]
      }
    }
  }
}
```

If enabled, LazyBoy will use ProForma to automatically generate several files from templates, whenever `composer update` or `composer install` is run. You are free to make modifications to the generated output; LazyBoy will not overwrite a file which already exists, so committing those changes to a VCS is safe and recommended. Having your VCS ignore the files will mean they are generated when you install vendors on a freshly cloned repository, however it means that you will always get the very latest version of the templates.

If you want to disable automatic file generation, so you can use LazyBoy classes on their own, you can either disable the ProForma plugin or add the following to your composer file:

```
{
  "extra": {
    "lexide/pro-forma": {
      "config": {
        "lexide/lazy-boy": {
          "preventTemplating": true
        }
      }
    }
  }
}
```

All that is left to do is create a vhost or otherwise point requests to `web/index.php`.

Code Generation
---------------

[](#code-generation)

### Application types

[](#application-types)

By default, LazyBoy will create files required for a standard REST application. It also supports adding console scripts, either as a straight Symfony Console app or integrated for AWS Lambda. In addition, you can replace the default REST application with one for AWS ApiGateway.

The application type is configured with ProForma config:

```
{
  "extra": {
    "lexide/pro-forma": {
      "config": {
        "lexide/lazy-boy": {
          "rest": false,
          "apiGateway": true,
          "lambda": true
        }
      }
    }
  }
}
```

This example would create an ApiGateway application with Lambda support

### Configuration

[](#configuration)

The full list of ProForma config options is as follows:

OptionData TypeDescriptionNotesrestboolCreate a REST applicationDefaults to trueapiGatewayboolCreate an AWS ApiGateway applicationMutually exclusive with "rest" which takes precedenceconsoleboolCreate a Symfony console applicationRequires symfony/console to be installedlambdaboolCreate an AWS Lambda applicationRequires symfony/console to be installeduseCorsboolDisable the LazyBoy Slim CORS middlewareOnly applicable to "rest" applicationsconsoleNamestringSet the name of the console applicationOnly applicable to "console" and "lambda" applicationspreventTemplatingboolDisable all code generation### Templates

[](#templates)

LazyBoy creates files from the following templates, base on the application types that are configured in `composer.json`

Template NameApplication TypeOutput LocationbootstrapAllapp/bootstrap.phpapiConfigREST, ApiGatewayapp/config/api.ymlconsoleConfig \*Console, Lambdaapp/config/console.ymlloggingConfigAllapp/config/logging.ymlroutesREST, ApiGatewayapp/config/routes.ymlserviceConfigAllapp/config/services.ymlconsole \*Consoleapp/consolelambda \*Lambdaapp/lambdaapiApiGatewayweb/api.phpindexRESTweb/index.php\* *This template depends on the `symfony/console` library being present in the package list*

Routing
-------

[](#routing)

### Routes

[](#routes)

If you are using the standard LazyBoy route loader, you can define your routes in YAML configuration files. Each route is defined as follows:

```
routes:
    route-name:
        url: "/sub/directory"
        method: "post"
        action:
          controller: "test_controller"
          method: "doSomething"
        public: true     # or
        security:
          # custom security parameters

```

`routes` is an associative array of routes that you want to allow access to.

In this case, a HTTP request that was `POST`ed to `/sub/directory`, would access a service in the container called `test-controller` and call its method `doSomething`. This route can be referenced as `route-name` when using the router.

For each route, the `url` and `action` parameters are required, but `method` is optional and defaults to `GET`.

Route URLs are processed by Slim, so you can add parameters and assertions [as you normally would](https://www.slimframework.com/docs/v4/objects/routing.html#route-placeholders) for that framework

```
    routes:
        route-one:
            url: "/user/{id:[0-9+]}"
            action:
              controller: "test_controller"
              method: "doSomething"
```

The URL `/user/56` would match and the `id` parameter would be set to `56`. The URL `/user/56/foo` or `/user/foo` would not match.

### Groups

[](#groups)

If you have many routes with similar URLs, such as:

- /users
- /users/{id}
- /users/login
- /users/logout

you can use a group to wrap them with a common url prefix.

```
groups:
    users:
        url: "/users"
        routes:
            user-list:
                # Omitting a route URL leaves the effective URL for this route as "/users"
                # ...
            get-user:
                url: "/{id}"
                # ...
            user-login:
                url: "/login"
                method: post
                # ...
            user-logout:
                url: "/logout"
                # ...
```

### Imports

[](#imports)

If you have a large API, it can be unwieldy to have all routes in the same file. Luckily, because LazyBoy uses syringe for route config, we can use imports to allow the routes file to be split up

```
imports:
  - "usersRoutes.yml"
  - "adminRoutes.yml"
  # ...

parameters:
  routes:
    otherRoutesAsNormal:
      # ...
```

```
# usersRoutes.yml
parameters:
  routes:
    userRoute:
      # ...
```

Syringe combines imported files using `array_replace_recursive()` so the only caveat to note is that you **MUST** use route names that are unique across all the route files. If not, the routes will get merged with unpredictable results.

### Controllers

[](#controllers)

A route must define a controller through which a matching request can be processed. These are PHP classes that include methods that will return a `Psr\Http\Message\ResponseInterface` when called.

Controller methods can be passed the Request, the initial Response object (for when middleware needs to add to a response) and any named parameters that slim parsed from the route URL. These values are assigned to method arguments that match the following criteria:

ArgumentCriteriaRequestHas the name `$request` or has the type `Psr\Http\Message\RequestInterface`ResponseHas the name `$response` or has the type `Psr\Http\Message\ResponseInterface`ParameterNamed for the URL parameter in question e.g. `$id` would match from the URL `/user/{id}`\*\* *URL Parameter types are not checked by LazyBoy; it is your responsibility to ensure the correct type is assigned*

LazyBoy also provides a `ResponseFactory` service which can be used as a convenient method of creating common responses, such as error responses, JSON responses, no-content responses, etc...

### Configuration

[](#configuration-1)

By default, the `RouteLoader` restricts HTTP methods to a set of the most commonly used methods: `GET`, `POST`, `PATCH`, `PUT` and `DELETE`. This can be customised by changing the Syringe DI configuration value `router.allowedMethods`:

```
parameters:
    # ...
    router.allowedMethods:
        - "get"
        - "post"
        - "delete"
        - "connect" # added CONNECT method
        - "upsert"  # added custom / non-standard method
        # PUT and PATCH methods are now disabled (not present in the list)
```

Allowed method values are case-insensitive.

API Middleware
--------------

[](#api-middleware)

### CORS Middleware

[](#cors-middleware)

The LazyBoy CORS middleware can be used to give your API the ability to accept cross domain requests. It is enabled by default and can be configured by changing the following parameters to your app's syringe config:

```
parameters:
  api.cors.allowedMethods: [] # List of allowed methods (defaults to the same methods as the router allows)
  api.cors.allowedOrigins: [] # List of allowed origin domains
  api.cors.allowedHeaders: [] # List of allowed headers
```

For allowed Origins and Headers, an empty list will insert `"*"` as the value in the CORS response headers. This is a fallback and not recommended for general use; if you're using CORS it should be configured only for the origins and headers that you need or your application may not be secure.

To disable the middleware, set "useCors" to false in ProForma config when generating code files, or remove the middleware from the Slim application in DI config.

### Security Middleware

[](#security-middleware)

LazyBoy has a security system that aims to prevent access to a non-public route unless specific conditions are met. LazyBoy itself only provides the ability to implement security controls; it makes no assumptions about what level of security you want or what services or data you use to provide it.

The system uses a series of Authorisers to run checks on a request to see if it should be allowed to continue. Each Authoriser implements the `AuthoriserInterface` and will be passed the request and the security context for a route. To implement an Authoriser, you should create a class implementing this interface and add the logic you require to validate a request. For example:

```
class RoleAuthoriser implements AuthoriserInterface
{

    protected $usersDao;

    public function __construct(UsersDao $usersDao)
    {
        $this->usersDao = $usersDao;
    }

    public function checkAuthorisation(RequestInterface $request, array $securityContext): bool
    {
        $route = $request->getAttribute("route");
        $userId = $route->getArgument("id");
        $user = $this->usersDao->getUser($userId);
        return $user->getRole() == $securityContext["role"];
    }

}
```

This authoriser gets a users ID from the request URL (via the route object), loads the user record from a Data Access Object and checks the user's role against the role that the route requires. If the two match then the check passes.

This is a convoluted example that we wouldn't expect to be used in a real system, but serves to show how authorisers work and the things they can do. As a general rule, a single Authoriser should check a single thing, so that they are composable and reusable.

Authorisers can be combined by using an `AuthoriserContainer`. This is itself an Authoriser, but one that loops over a list of other Authorisers, checking the request against each one in turn. It has two modes, "requireAll" and "requireOne", which determines which of the Authorisers need to pass before returning its own result. "requireAll" is similar to a logical AND operation, whereas "requireOne" is a logical OR

Using containers, Authorisers can be chained and combined in complex ways, allowing complete flexibility in applying your security requirements. LazyBoy sets up a default AuthoriserContainer, which you can use by adding the `api.authorisers`tag to your Authoriser service definition:

```
services:
  myAuthoriser:
    class: MyAuthoriser
    tags:
      - "api.authorisers"
```

Alternatively, you can use your own authoriser by replacing the `api.authoriser` service definition

Lambda
------

[](#lambda)

In order to configure a Lambda function to run a `symfony/console` command, there are specific environment variables that need to be set in the Lambda configuration:

variableDescriptionCOMMANDThe console command nameARGSA JSON encoded string of key value pairs to use for the commands argumentsOPTIONSA JSON encoded string of key value pairs to use for the commands optionsLambdas can be set up to parse values from an event into CLI arguments and options that a command can use. Use the syntax `"{event.myEventProperty}"` to inject the value of `myEventProperty` into the specific argument or option

For example, a Lambda set up with the following environment variables:

```
COMMAND='lexide:lazy-boy:example-command'
ARGS='{"foo":"one","bar":"{event.bar}"}'
OPTIONS='{"--baz":"{event.baz}","-f":"four"}'

```

And using the event JSON:

```
{
  "bar": "two",
  "baz": "three"
}
```

Will generate an input object equivalent to:

```
lexide:lazy-boy:example-command 'one' 'two' --baz='three' -f 'four'

```

### Empty and missing event properties

[](#empty-and-missing-event-properties)

When templating event properties into arguments, the values must not be missing or null, but can be other empty values. Arguments are assumed to be required and must always have a value to insert into the command; an exception is thrown for invalid or missing values.

For options, `null` is a valid value, signifying that the option is a flag and does not take a value. However, if an event property is missing completely, the option is omitted from the command completely.

Logging
-------

[](#logging)

LazyBoy provides a stub service to allow logging, found in the `logging.yml` DI config file. It is integrated into the generated code using the PSR-3 `Psr\Log\LoggerInterface`, but you will need to set up your own logger in order for errors and other logs to be handled correctly

Contributing
------------

[](#contributing)

If you have improvements you would like to see, open an issue in this github project or better yet, fork the project, implement your changes and create a pull request.

The project uses [PSR-12](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-12-extended-coding-style-guide.md) code styles and we insist that these are strictly adhered to. Also, please make sure that your code works with php 8.0.

Why "LazyBoy"?
--------------

[](#why-lazyboy)

Because it likes REST, of course :)

###  Health Score

53

—

FairBetter than 97% of packages

Maintenance81

Actively maintained with recent releases

Popularity21

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity82

Battle-tested with a long release history

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~83 days

Recently: every ~73 days

Total

49

Last Release

98d ago

Major Versions

0.3.0 → 1.0.02015-11-05

1.1.1 → 2.0.02015-11-20

2.5.0 → 3.0.02018-02-20

3.1.0 → 4.0.02024-10-02

PHP version history (4 changes)0.1.0PHP &gt;=5.3.0

1.1.0PHP &gt;=5.4.0

3.1.0PHP &gt;=7.3.0

4.0.0PHP &gt;=8.0.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/a01f73f5eea8a755e31d879b50f5862ee8a20e8d73daba1c15cb7e75e68796db?d=identicon)[downsider](/maintainers/downsider)

---

Top Contributors

[![Danny-Smart](https://avatars.githubusercontent.com/u/113538899?v=4)](https://github.com/Danny-Smart "Danny-Smart (91 commits)")[![downsider](https://avatars.githubusercontent.com/u/4508388?v=4)](https://github.com/downsider "downsider (89 commits)")[![dochne](https://avatars.githubusercontent.com/u/1678803?v=4)](https://github.com/dochne "dochne (8 commits)")[![oliveremberton](https://avatars.githubusercontent.com/u/4114396?v=4)](https://github.com/oliveremberton "oliveremberton (3 commits)")[![andywaite](https://avatars.githubusercontent.com/u/6773151?v=4)](https://github.com/andywaite "andywaite (1 commits)")[![B3none](https://avatars.githubusercontent.com/u/24966460?v=4)](https://github.com/B3none "B3none (1 commits)")[![leepercox](https://avatars.githubusercontent.com/u/6492146?v=4)](https://github.com/leepercox "leepercox (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/lexide-lazy-boy/health.svg)

```
[![Health](https://phpackages.com/badges/lexide-lazy-boy/health.svg)](https://phpackages.com/packages/lexide-lazy-boy)
```

###  Alternatives

[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.4k5.6M651](/packages/sylius-sylius)[algolia/algoliasearch-client-php

API powering the features of Algolia.

69433.0M114](/packages/algolia-algoliasearch-client-php)[theodo-group/llphant

LLPhant is a library to help you build Generative AI applications.

1.5k311.5k5](/packages/theodo-group-llphant)[commercetools/commercetools-sdk

The official PHP SDK for the commercetools Composable Commerce APIs

19281.5k](/packages/commercetools-commercetools-sdk)[php-heroku-client/php-heroku-client

A PHP client for the Heroku Platform API

24404.8k4](/packages/php-heroku-client-php-heroku-client)[rubix/server

Deploy your Rubix ML models to production with scalable stand-alone inference servers.

632.3k](/packages/rubix-server)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
