Intro

Configuration


    // configuration values may be accessed using "dot" syntax
    // includes the name of the file and option you wish to access
    $value = config('app.locale');
    // to set configuration values at runtime, pass an array to the config helper
    config(['app.locale' => 'en']);
    // retrieve the values of environment variables
    $debug = env('APP_DEBUG', true); // true is default value

    // current application environment,
    // determined in .env file via APP_ENV variable
    $environment = app()->environment();
    // environment is local
    if (app()->environment('local')) { /* ... */ }
    // environment is either local OR staging
    if (app()->environment('local', 'staging')) { /* ... */ }
  

Routing


    $router->get($uri, $callback);
    $router->post($uri, $callback);
    $router->put($uri, $callback);
    $router->patch($uri, $callback);
    $router->delete($uri, $callback);
    $router->options($uri, $callback);

    // most basic Lumen routes simply accept a URI and a Closure
    $router->get('foo', function () {
      return 'Hello World';
    });
    $router->post('foo', function () {
      //
    });

    // --- REQUIRED PARAMETERS
    $router->get(
      'user/{id}',
      function ($id) { return 'User '.$id; }
    );
    $router->get(
      'posts/{postId}/comments/{commentId}',
      function ($postId, $commentId) { /* ... */ }
    );

    // --- OPTIONAL PARAMETERS
    // enclosing part of the route URI definition in [...]
    // /foo[bar] will match both /foo and /foobar
    // only supported in a trailing position of the URI
    // dont place an optional parameter in the middle of a route definition:
    $router->get('user[/{name}]', function ($name = null) {
      return $name;
    });

    // --- RegEx Constraints
    $router->get(
      'user/{name:[A-Za-z]+}',
      function ($name) { /* ... */ }
    );

    // --- NAMED ROUTES
    // convenient generation of URLs or redirects for specific routes
    // specify a name for a route using the as array key when defining the route
    $router->get(
      'profile', [
        'as' => 'profile',
        function () { /* ... */ }
    ]);
    // route names for controller actions
    $router->get('profile', [
      'as' => 'profile',
      'uses' => 'UserController@showProfile'
    ]);
    // generating URLs/redirects to named routes
    $url = route('profile');
    return redirect()->route('profile')
    // with parameters
    $router->get('user/{id}/profile', [
      'as' => 'profile',
      function ($id) { /* ... */ }
    ]);
    // ...
    $url = route('profile', ['id' => 1]);

    // --- --- ROUTE GROUPS
    // share route attributes: middleware, namespaces, across number of routes
    // without needing to define those attributes on each individual route
    // --- middleware
    $router->group(
      ['middleware' => 'auth'],
      function () use ($router) {
        $router->get('/', function () {
          // Uses Auth Middleware
        });
        $router->get('user/profile', function () {
          // Uses Auth Middleware
        });
    });
    // --- NAMESPACES
    $router->group(
      ['namespace' => 'Admin'],
      function() use ($router) {
        // Using The "App\Http\Controllers\Admin" Namespace...
        $router->group(['namespace' => 'User'], function() use ($router) {
          // Using The "App\Http\Controllers\Admin\User" Namespace...
        });
    });
    // --- ROUTE PREFIXES
    $router->group(
      ['prefix' => 'admin'],
      function () use ($router) {
        $router->get('users', function ()    {
          // Matches The "/admin/users" URL
        });
    });
    // specify common parameters for grouped routes
    $router->group(
      ['prefix' => 'accounts/{accountId}'],
      function () use ($router) {
        $router->get('detail', function ($accountId)    {
          // Matches The "/accounts/{accountId}/detail" URL
        });
    });
    // ...
    Route::prefix('admin')->group(function () {
      Route::get('users', function () {
        // matches The "/admin/users" URL
      });
    });
    // route name prefixes - prefix each route name in the group with a given string
    Route::name('admin.')->group(function () {
      Route::get('users', function () {
        // route assigned name "admin.users"...
      })->name('users');
    });

    // --- ROUTE MODEL BINDING
    // instead of injecting a ID, inject the entire model instance that matches
    // Laravel automatically resolves Eloquent models defined in routes or controller actions
    // whose type-hinted variable names match a route segment name
    // otherwise 404 HTTP response generated
    // --- IMPLICIT BINDING
    Route::get('api/users/{user}', function (App\User $user) {
      return $user->email;
    });
    // specify the column in the route parameter definition
    Route::get('api/posts/{post:slug}', function (App\Post $post) {
      return $post;
    });
    // getRouteKeyName, in the Eloquent model
    // use a database column other than id when retrieving a given model class
    // ...
      public function getRouteKeyName() {
        return 'slug';
      }
    // scope the second Eloquent model such that it must be a child of the previous
    // retrieves a blog post by slug for a specific user:
    // retrieve nested model by its parent using conventions to guess the relationship
    // User model has a relationship named posts (the plural form of the route parameter name) used to retrieve
    Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) {
      return $post;
    });
      // resolveChildRouteBinding method will be used to resolve the child binding of the parent model:
      // ...
      // retrieves the child model for a bound value
      public function resolveChildRouteBinding($childType, $value, $field) {
          return parent::resolveChildRouteBinding($childType, $value, $field);
      }
    // missing model behavior
    Route::get('/locations/{location:slug}', [LocationsController::class, 'show'])
      ->name('locations.view')
      ->missing(function (Request $request) {
        return Redirect::route('locations.index');
      });

    // --- EXPLICIT BINDING
    // define in boot method of the RouteServiceProvider class
    // ...
      public function boot() {
        parent::boot();
        Route::model('user', App\User::class);
      }
    // ...
    // next, define a route that contains a {user} parameter
    Route::get(
      'profile/{user}',
      function (App\User $user) {
        // ...
      }
    );
    // we have bound all {user} parameters to the App\User model,
    // a User instance will be injected into the route
    // request to profile/1 will inject the User instance from the database with ID of 1
    // --- Route::bind - own resolution logic
    // ...
      public function boot() {
        parent::boot();
        Route::bind('user', function ($value) { // value of the URI segment
            // return instance of the class that should be injected into the route
            return App\User::where('name', $value)->firstOrFail();
          }
        );
      }
    // ...
    // alternatively, override the resolveRouteBinding method on Eloquent model
    // retrieve the model for a bound value
    public function resolveRouteBinding$value, $field) { // value of the URI segment
      // return instance of the class that should be injected into the route
      return $this->where('name', $value)->firstOrFail();
    }

    // --- information about the route handling the incoming request
    $route = Route::current();
    $name = Route::currentRouteName();
    $action = Route::currentRouteAction();
  

Middleware (CORS)

Definition

    namespace App\Http\Middleware;
    use Closure;
    class HowOldMiddleware {
      public function handle($request, Closure $next) {
        // if the given age is less than or equal to 200
        if ($request->input('age') <= 200) {
          // return an HTTP redirect to the client
          return redirect('home');
        }
        // otherwise, the request will be passed further into the application
        return $next($request);
      }
    }

    // ...
      public function handle($request, Closure $next)
      {
        // ... perform action before the request is handled
        $response = $next($request);
        // ... perform action after the request is handled
        return $response;
      }
    // ...

    // --- receive additional custom parameters,  passed after $next
    // ...
      public function handle($request, Closure $next, $role) {
        if (! $request->user()->hasRole($role)) {
          // Redirect...
        }
        return $next($request);
      }
    // ...
    // OR specified when defining the route by separating the middleware name
    // and parameters with a : , multiple parameters should be delimited by commas:
    $router->put(
      'post/{id}',
      ['middleware' => 'role:editor', function ($id) { /* ... */ }
    ]);
    Route::put('post/{id}', function ($id) {
      // ...
    })->middleware('role:editor');

    // --- terminable Middleware
    // do some work after the HTTP response has already been sent to the browser.
    // add it to the list of global middleware in bootstrap/app.php
    namespace Illuminate\Session\Middleware;
    use Closure;
    class StartSession {
      public function handle($request, Closure $next) {
        return $next($request);
      }
      public function terminate($request, $response) {
        // Store the session data...
      }
    }
    // when calling the terminate method on middleware,
    // Lumen will resolve a fresh instance of the middleware from the service container
    // if you want to use the same middleware instance
    // when the "handle" and "terminate" methods are called,
    // register the middleware with the container using the container "singleton" method
  
Usage

    // --- global middleware
    // run during every HTTP request to application,
    // list in call to the $app->middleware() method in bootstrap/app.php
    $app->middleware([
      App\Http\Middleware\HowOldMiddleware::class
    ]);
    // OR, in Laravel, list the middleware class
    // in the $middleware property of your app/Http/Kernel.php class

    // --- assigning middleware to routes
    // first assign the middleware a short-hand key in bootstrap/app.php
    $app->routeMiddleware([
    // protected $routeMiddleware = [ // within App\Http\Kernel class
      'auth' => App\Http\Middleware\Authenticate::class,
      'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
      'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
      'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
      'can' => \Illuminate\Auth\Middleware\Authorize::class,
      'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
      'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
      'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
      'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ]);
    // use the middleware key in the route options array:
    $router->get( 'admin/profile', [
      'middleware' => 'auth',
      function () { /* ... */ }
    ]);
    // use an array to assign multiple middleware to the route:
    $router->get( '/', [
      'middleware' => ['first', 'second'],
      function () { /* ... */ }
    ]);
    // in Laravel
    Route::get('admin/profile', function () {
      // ...
    })->middleware('auth');
    Route::get('/', function () {
      // ...
    })->middleware('first', 'second');
    // pass the fully qualified class name
    use App\Http\Middleware\CheckAge;
    Route::get('admin/profile', function () {
      // ...
    })->middleware(CheckAge::class);

    // --- middleware groups
    protected $middlewareGroups = [
      // out of the box, the web middleware group is automatically applied
      // to routes/web.php file by the RouteServiceProvider
      'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
      ],
      'api' => [
        'throttle:60,1',
        'auth:api',
      ],
    ];
    // ...
    Route::get('/', function () {
      // ...
    })->middleware('web');
    Route::group(['middleware' => ['web']], function () {
      // ...
    });

    // --- sorting middleware - execute in a specific order
    protected $middlewarePriority = [
      \Illuminate\Session\Middleware\StartSession::class,
      \Illuminate\View\Middleware\ShareErrorsFromSession::class,
      \App\Http\Middleware\Authenticate::class,
      \Illuminate\Session\Middleware\AuthenticateSession::class,
      \Illuminate\Routing\Middleware\SubstituteBindings::class,
      \Illuminate\Auth\Middleware\Authorize::class,
    ];
  
CORS (5.8-6.0)

    // --- app/app/Http/Middleware/CorsMiddleware.php
    namespace App\Http\Middleware;
    use Closure;
    class CorsMiddleware {
      public function handle($request, Closure $next) {
        $headers = [
          'Access-Control-Allow-Origin'      => '*',
          'Access-Control-Allow-Methods'     => 'POST, GET, OPTIONS, PUT, DELETE',
          'Access-Control-Allow-Credentials' => 'true',
          'Access-Control-Max-Age'           => '86400',
          'Access-Control-Allow-Headers'     => 'Content-Type, Authorization, X-Requested-With'
        ];
        if ($request->isMethod('OPTIONS')) {
          return response()->json('{"method":"OPTIONS"}', 200, $headers);
        }
        $response = $next($request);
        foreach($headers as $key => $value) {
          $response->header($key, $value);
        }
        return $response;
      }
    }

    // --- app/bootstrap/app.php
    $app->middleware([
      App\Http\Middleware\CorsMiddleware::class
    ]);
  

Controller


    namespace App\Http\Controllers;
    use App\User;
    class UserController extends Controller {
      // retrieve the user for the given ID
      public function show($id) {
        return User::findOrFail($id);
      }
    }
    // ... its route
    $router->get(
      'user/{id}',
      'UserController@show'
    );

    // --- nest or organize controllers deeper
    // using PHP namespaces into the App\Http\Controllers directory
    // use the specific class name relative to the App\Http\Controllers root namespace
    $router->get(
      'foo',
      // App\Http\Controllers\Photos\AdminController
      'Photos\AdminController@method'
    );

    // --- naming controller routes
    $router->get(
      'foo', [
        'uses' => 'FooController@method',
        'as' => 'named'
    ]);
    // generate URL to named controller route
    $url = route('named');

    // --- RESOURCE ROUTES

    // register a resourceful route to the controller
    // add route to new method separately, before resource registration
    Route::get('photos/bar', 'PhotoController@bar');
    Route::resource(
      'photos',
      'PhotoController'
    );
    // register many resource controllers at once
    Route::resources([
      'photos' => 'PhotoController',
      'posts' => 'PostController'
    ]);
    // specify a subset of actions the controller should handle
    // instead of the full set of default actions
    Route::resource(
      'photos',
      'PhotoController'
    )->only([ 'index', 'show' ]);
    Route::resource(
      'photos',
      'PhotoController'
    )->except([ 'create', 'store', 'update', 'destroy' ]);
    // for API resource routes,
    // exclude routes that present HTML templates: create, edit
    Route::apiResource(
      'photos',
      'PhotoController'
    );
    Route::apiResources([
      'photos' => 'PhotoController',
      'posts' => 'PostController'
    ]);
    // --- naming resource routes
    Route::resource('photos', 'PhotoController')->names([
      'create' => 'photos.build'
    ]);
    // naming resource route parameters
    Route::resource('users', 'AdminUserController')->parameters([
      'users' => 'admin_user' // resource names and parameter names
    ]);
    // generates /users/{admin_user} for the resource show route

    // --- localizing resource URIs in the boot method of AppServiceProvider
    // ...
      Route::resourceVerbs([
        'create' => 'crear',
        'edit' => 'editar',
      ]);
    // ...
    // Route::resource('fotos', 'PhotoController'), will produce:
    // /fotos/crear
    // /fotos/{foto}/editar

    // --- ASSIGN MIDDLEWARE

    $router->get('profile', [
      'middleware' => 'auth',
      'uses' => 'UserController@showProfile'
    ]);
    // OR within controller constructor
    class UserController extends Controller {
      // instantiate a new UserController instance
      public function __construct() {
        $this->middleware('auth');
        $this->middleware('log', ['only' => [
          'fooAction',
          'barAction',
        ]]);
        $this->middleware('subscribed', ['except' => [
          'fooAction',
          'barAction',
        ]]);
      }
    }

    // --- SINGLE ACTION CONTROLLERS

    namespace App\Http\Controllers;
    use App\User;
    use App\Http\Controllers\Controller;
    class ShowProfile extends Controller {
      // how the profile for the given user
      public function __invoke($id) {
        return view('user.profile', ['user' => User::findOrFail($id)]);
      }
    }
    // then, no need to specify a method
    Route::get('user/{id}', 'ShowProfile');
  

Actions Handled By Resource Controller

Verb URI Action Route Name
GET /photos index photos.index
GET /photos/create create photos.create
POST /photos store photos.store
GET /photos/{photo} show photos.show
GET /photos/{photo}/edit edit photos.edit
PUT/PATCH /photos/{photo} update photos.update
DELETE /photos/{photo} destroy photos.destroy

Dependency Injection & Controllers


    // --- constructor injection
    namespace App\Http\Controllers;
    use App\Repositories\UserRepository;
    class UserController extends Controller {
      // user repository instance
      protected $users;
      // create a new controller instance
      // type-hint dependency
      public function __construct(UserRepository $users) {
        $this->users = $users;
      }
    }

    // --- method injection
    namespace App\Http\Controllers;
    use Illuminate\Http\Request;
    class UserController extends Controller {
      // store a new user
      // type-hint the Illuminate\Http\Request instance
      public function store(Request $request) {
        $name = $request->input('name');
        // ...
      }
      // update specified user
      // type-hint the Illuminate\Http\Request
      // and access route parameter id
      // for $router->put('user/{id}', 'UserController@update')
      public function update(Request $request, $id) {
        // ...
      }
    }
  

Request


    namespace App\Http\Controllers;
    use Illuminate\Http\Request;
    class UserController extends Controller {
      public function store(Request $request) {
        $name = $request->input('name'); // ...
      }
      // also expecting input from a route parameter
      // $router->put('user/{id}', 'UserController@update')
      // Route::put('user/{id}', 'UserController@update');
      public function update(Request $request, $id) {
        // ...
      }
    }

    // --- path - request URI
    $uri = $request->path(); // for http://domain.com/foo/bar is foo/bar

    // --- is - verify URI fo pattern match
    // use the * character as a wildcard
    if ($request->is('admin/*')) { /* ... */ }

    // --- full URL without query string
    $url = $request->url();
    // --- full URL with query string
    $url = $request->fullUrl();

    // --- HTTP verb for the request
    $method = $request->method();
    // --- verify for HTTP verb
    if ($request->isMethod('post')) { /* ... */ }

    // --- RETRIEVING INPUT
    $name = $request->input('name'); // same way for all verbs
    $name = $request->input('name', 'Default Value');
    // use "dot" notation to access the arrays
    $name = $request->input('products.0.name');
    $names = $request->input('products.*.name');
    // determine value presense
    if ($request->has('name')) { /* ... */ }
    // determine if all of the specified values are present in array
    if ($request->has(['name', 'email'])) { /* ... */ }
    // if a value is present on the request and is not empty
    if ($request->filled('name')) { /* ... */ }
    // all input data as an array
    $input = $request->all();
    // input from the query string
    $name = $request->query('name');
    $name = $request->name; // using dynamic properties
    $name = $request->query('name', 'Helen'); // with default value
    $query = $request->query(); // all string values as an associative array
    // dig into JSON arrays
    $name = $request->input('user.name');
    // retrieve a sub-set of the input data: "only" and "except"
    // accept a single array or a dynamic list of arguments:
    $input = $request->only(['username', 'password']);
    $input = $request->only('username', 'password');
    $input = $request->except(['credit_card']);
    $input = $request->except('credit_card');

    // --- RETRIEVING FILES
    $file = $request->file('photo');
    $file = $request->photo;
    // if a file is present on the request using the hasFile method
    if ($request->hasFile('photo')) { /* ... */ }
    // verify that there were no problems uploading the file
    if ($request->file('photo')->isValid()) { /* ... */ }
    // fully-qualified path and its extension
    $path = $request->photo->path();
    // guess the file extension based on its contents
    $extension = $request->photo->extension();
    // move the uploaded file to a new location
    $request->file('photo')->move($destinationPath);
    $request->file('photo')->move($destinationPath, $fileName);
    $path = $request->photo->store('images');
    $path = $request->photo->store('images', 's3'); // with disk that should be used
    $path = $request->photo->storeAs('images', 'filename.jpg');
    $path = $request->photo->storeAs('images', 'filename.jpg', 's3');

    // --- PSR-7 requests:
    // composer require symfony/psr-http-message-bridge
    // composer require zendframework/zend-diactoros
    use Psr\Http\Message\ServerRequestInterface;
    $router->get(
      '/',
      function (ServerRequestInterface $request) { /* ... */ }
    );
    // if you return a PSR-7 response instance from a route or controller,
    // it will automatically be converted back to a Laravel response instance
    // and be displayed by the framework
  

Response


    use Illuminate\Http\Response;

    $router->get('home', function () {
      return (new Response($content, $status))
        ->header('Content-Type', $value);
      // OR response helper
      return response($content, $status)
        ->header('Content-Type', $value);
    });

    // --- CHAIN response methods
    return response($content)
      ->header('Content-Type', $type)
      ->header('X-Header-One', 'Header Value')
      ->header('X-Header-Two', 'Header Value');
    return response($content)
      ->withHeaders([
        'Content-Type' => $type,
        'X-Header-One' => 'Header Value',
        'X-Header-Two' => 'Header Value',
      ]);

    // --- return as JSON
    return response()->json(
      ['name' => 'Abigail', 'state' => 'CA']
    );
    // provide a status code and an array of additional headers
    return response()->json(
      ['error' => 'Unauthorized'],
      401,
      ['X-Header-One' => 'Header Value']
    );
    // create a JSONP response
    return response()
      ->json(['name' => 'Abigail', 'state' => 'CA'])
      ->setCallback($request->input('callback'));

    // --- DOWNLOAD the file at the given path
    return response()->download($pathToFile);
    return response()->download(
      $pathToFile,
      $name_to_show,
      $headers
    );
    return response()->download($pathToFile)->deleteFileAfterSend();
    // file Response
    return response()->file($pathToFile);
    return response()->file($pathToFile, $headers);

    // --- REDIRECT
    $router->get('dashboard', function () {
      return redirect('home/dashboard');
    });
    // redirecting to named routes
    return redirect()->route('login');
    return redirect()->route('profile', ['id' => 1]); // profile/{id}
    // redirecting to a route with an "id" parameter
    // that is being populated from an Eloquent model
    // pass the model itself, "id" will be extracted automatically:
    return redirect()->route('profile', [$user]);

    // simple response, in route
    $router->get('/', function () {
      return 'Hello World';
    });
  

Authentication


    // AuthServiceProvider
    // ...
      $this->app['auth']->viaRequest('api', function ($request) {
        // return User or null...
        if ($request->input('api_token')) {
            return User::where('api_token', $request->input('api_token'))->first();
        }
      });
    // ...

    // return an instance of the Illuminate\Auth\GenericUser class
    use Illuminate\Auth\GenericUser;
    // ...
    return new GenericUser(['id' => 1, 'name' => 'Taylor']);

    // authenticated User
    $router->get(
      '/post/{id}',[
        'middleware' => 'auth',
        function (Request $request, $id) {
          $user = Auth::user(); // or ...
          $user = $request->user();
      }]
    );
  

Authorization


    // --- optional user (guest)
    // registered policy "update" method
    // declaring an "optional" type-hint
    // or supplying a null default value for the user argument definition
    space App\Policies;
    use App\User;
    use App\Post;
    class PostPolicy {
      // determine if the given post can be updated by the user
      public function update(?User $user, Post $post) {
        return $user->id === $post->user_id;
      }
    }
  

gates


    use App\Models\User;
    use App\Models\Category;
    use Illuminate\Auth\Access\Response;
    use Illuminate\Support\Facades\Gate;

    // register any authentication / authorization services
    // app/Providers/AuthServiceProvider
    public function boot() {
      $this->registerPolicies();
      Gate::define(
        'update-post',
        function ($user, $post) {
          return $user->id == $post->user_id;
      });
      // OR
      Gate::define(
        'update-post',
        'App\Policies\PostPolicy@update'
      );
    }

    // - allows , denies - authorize an action using gates,
    // currently authenticated user is automatically passed to callback
      if (Gate::allows('update-post', $post)) {
        // current user can update the post
      }
      if (Gate::denies('update-post', $post)) {
        // current user can't update the post
        abort(403);
      }
      // if (Gate::denies(['update-post','...'], $post)) {
    // - any , none - authorize multiple actions at a time using the any or none methods:
      if (Gate::any(['update-post', 'delete-post'], $post)) {
        // user can update or delete the post
      }
      if (Gate::none(['update-post', 'delete-post'], $post)) {
        // user can't update or delete the post
      }

    // - authorize - throws an AuthorizationException (403 HTTP) if the action is not authorized
      Gate::authorize('edit-settings');
      Gate::authorize('update-post', $post);

    // - forUser - determine if a particular user is authorized to perform an action
      if (Gate::forUser($user)->allows('update-post', $post)) {
        // user can update the post
      }
      if (Gate::forUser($user)->denies('update-post', $post)) {
        // user can't update the post
      }

    // - supplying additional context
    // allows, denies, check, any, none, authorize, can, cannot and Blade directives @can, @cannot, @canany
    // can receive an array as their second argument
    // they are passed as parameters to the gate closure, can be used for additional context when making authorization decisions
      Gate::define('create-post', function (User $user, Category $category, $pinned) {
        if (! $user->canPublishToGroup($category->group)) {
          return false;
        } elseif ($pinned && ! $user->canPinPosts()) {
          return false;
        }
        return true;
      });
      if (Gate::check('create-post', [$category, $pinned])) {
        // The user can create the post...
      }

    // - before - define a callback that is run before all other authorization checks
    // non-null result, considered the result of the check
      Gate::before(function ($user, $ability) {
        if ($user->isSuperAdmin()) {
          return true;
        }
      });
    // - after - define a callback to be executed after all other authorization checks
      Gate::after(function ($user, $ability, $result, $arguments) {
        if ($user->isSuperAdmin()) {
          return true;
        }
      });

    // --- gates responses - return detailed response, including an error message
      // ...
      Gate::define('edit-settings', function (User $user) {
        return $user->isAdmin
          ? Response::allow()
          : Response::deny('You must be an administrator.');
      });

    // get the full authorization response returned by the gate
      $response = Gate::inspect('edit-settings');
      // ...
      if ($response->allowed()) {
          // The action is authorized...
      } else {
          echo $response->message();
      }
  

policies

Controller Method Policy Method
index viewAny
show view
create create
store create
edit update
update update
destroy delete

    // php artisan make:policy PostPolicy
    // php artisan make:policy PostPolicy --model=Post

    namespace App\Providers;
    use App\Post;
    use App\Policies\PostPolicy;
    use Illuminate\Support\Facades\Gate;
    use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
    class AuthServiceProvider extends ServiceProvider {
      // policy mappings for the application
      protected $policies = [
        Post::class => PostPolicy::class,
      ];
      // register any application authentication / authorization services
      public function boot() {
        $this->registerPolicies();
        // ...
        // in Lumen, where no $policies available
        Gate::policy(Post::class, PostPolicy::class);
      }
    }

    // --- before - executed before any other methods on the policy
      public function before($user, $ability) {
        if ($user->isSuperAdmin()) {
          return true;
        }
      }

    // own policy discovery logic
    use Illuminate\Support\Facades\Gate;
    // ...
      Gate::guessPolicyNamesUsing(function ($modelClass) {
        // return policy class name...
      });
    // ...

    // --- authorizing "create" action
    // only receive the currently authenticated user
    // and not an instance of the model they authorize
    // determine if the given user can create posts
      public function create(User $user) {
        // ...
      }

    // --- AUTHORIZING ACTIONS USING POLICIES

    // - User model included with app has "can" and "cant" authorizing actions
    // receives authorize action and relevant model
    // determine if a user is authorized to update a given Post model
      if ($user->can('update', $post)) {
        //
      }
    // pass a class name to the can method
    // used to determine which policy to use when authorizing the action
      if ($user->can('create', Post::class)) { // use App\Post;
        // executes the "create" method on the relevant policy...
      }

      if ($request->user()->can('update-post', $post)) {
        // user is allowed to update the post...
      }
      if ($request->user()->cannot('update-post', $post)) {
        abort(403);
      }

    // - included Laravel middleware
    // authorize actions before the incoming request reaches routes or controllers
    // by default Illuminate\Auth\Middleware\Authorize middleware
    // is assigned the "can" key in App\Http\Kernel class
    use App\Post;
    // ...
      Route::put('/post/{post}', function (Post $post) {
        // current user may update the post...
      })->middleware(
        // name of the actionto authorize
        // and route parameter to pass to the policy method
        'can:update,post'
      );
    // actions like "create" may not require a model instance
    // pass a class name, to determine which policy for action authorization
      Route::post('/post', function () {
        // The current user may create posts...
      })->middleware('can:create,App\Post');

    // - controller helpers, "authorize" method of any controllers
    // which extend the App\Http\Controllers\Controller base class
    // accepts the name of the action you wish to authorize and the relevant model
    // throws an Illuminate\Auth\Access\AuthorizationException (403 status code)
    namespace App\Http\Controllers;
    use App\Post;
    use Illuminate\Http\Request;
    use App\Http\Controllers\Controller;
    class PostController extends Controller {

      // update the given blog post
      public function update(Request $request, Post $post) {
        $this->authorize('update', $post);
        // current user can update the blog post...
      }

      // pass a class name to the authorize method in "create"
      // which not requires a model instance
      // create a new blog post
      public function create(Request $request) {
        $this->authorize('create', Post::class);
        // current user can create blog posts...
      }

      // use authorizeResource method in resource controllers constructor
      // will attach the appropriate "can" middleware definition
      // accepts the model class name as its first argument,
      // and the name of the route/request parameter
      // that will contain the model ID as its second argument
      public function __construct() {
        $this->authorizeResource(Post::class, 'post');
      }

    }

    // - blade templates
    // display a portion of the page if user is authorized to perform action
    @can('update', $post)
      <!-- current user can update the post -->
    @elsecan('create', App\Post::class)
      <!-- current user can create new post -->
    @endcan
    @cannot('update', $post)
      <!-- current user cant update the post -->
    @elsecannot('create', App\Post::class)
      <!-- current user cant create new post -->
    @endcannot
    // ... are shortcuts for writing @if and @unless statements
    @if (Auth::user()->can('update', $post))
      <!-- current user can update the post -->
    @endif
    @unless (Auth::user()->can('update', $post))
      <!-- current user cant update the post -->
    @endunless

    // pass a class name to the @can and @cannot directives
    // if the action does not require a model instance
    @can('create', App\Post::class)
      <!-- current user can create posts -->
    @endcan
    @cannot('create', App\Post::class)
      <!-- current user cant create posts -->
    @endcannot
  

Cache


    // setup a table to contain the cache items
    Schema::create('cache', function ($table) {
        $table->string('key')->unique();
        $table->text('value');
        $table->integer('expiration');
    });
    // "php artisan cache:table" - generate a migration with the proper schema
  

    namespace App\Http\Controllers;
    // using Cache facade, convenient, terse access
    // to the underlying implementations of the cache contracts
    use Illuminate\Support\Facades\Cache;
    class UserController extends Controller {
      // show a list of all users of the application
      public function index() {
        $value = Cache::get('key');
        //
      }
    }

    // --- accessing multiple cache stores
    // Cache::store(key) - addresses one of the stores
    // listed in the stores configuration array in cache configuration file
    $value = Cache::store('file')->get('foo');
    Cache::store('redis')->put('bar', 'baz', 600); // 10 Minutes

    // --- RETRIEVE
    $value = Cache::get('key'); // null, if does not exists
    $value = Cache::get('key', 'default_value');
    // with Closure
    $value = Cache::get('key', function () {
      return DB::table(...)->get();
    });
    // checking
    if (Cache::has('key')) { /* ... */ }
    // incrementing / decrementing
    Cache::increment('key');
    Cache::increment('key', $amount);
    Cache::decrement('key');
    Cache::decrement('key', $amount);
    // retrieve & store
    $value = Cache::remember('users', $seconds, function () {
      return DB::table('users')->get();
    });
    // retrieve an item from the cache or store it forever
    $value = Cache::rememberForever('users', function () {
      return DB::table('users')->get();
    });
    // retrieve & delete, null returned if the item does not exist
    $value = Cache::pull('key');

    // --- STORE
    Cache::put('key', 'value', $seconds);
    Cache::put('key', 'value'); // store indefinitely
    // expiration time of the cached item
    Cache::put('key', 'value', now()->addMinutes(10));
    // store if not present
    // return true if item is added, otherwise return false
    Cache::add('key', 'value', $seconds);
    // store an item in the cache permanently
    // must be manually removed from the cache using the "forget" method
    // for Memcached driver, this items may be removed when the cache reaches its size limit
    Cache::forever('key', 'value');

    // --- REMOVE
    Cache::forget('key');
    Cache::put('key', 'value', 0);
    Cache::put('key', 'value', -5); // negative TTL
    // clear the entire cache
    Cache::flush();
  

atomic locks


    // manipulation of distributed locks without worrying about race conditions
    // use memcached, dynamodb, or redis cache driver as default cache driver
    // all servers must be communicating with the same central cache server
    use Illuminate\Support\Facades\Cache;
    // ...
      $lock = Cache::lock('foo', 10);
      if ($lock->get()) {
        // lock acquired for 10 seconds...
        $lock->release();
      }
    // release the lock automatically after the Closure is executed
    Cache::lock('foo')->get(function () {
      // lock acquired indefinitely and automatically released...
    });
    // --- if the lock is not available at the request moment
    // instruct Laravel to wait for a specified number of seconds
    // if the lock can not be acquired within the specified time limit,
    // an Illuminate\Contracts\Cache\LockTimeoutException will be thrown
    use Illuminate\Contracts\Cache\LockTimeoutException;
    // ...
      $lock = Cache::lock('foo', 10);
      try {
        $lock->block(5);
        // lock acquired after waiting maximum of 5 seconds...
      } catch (LockTimeoutException $e) {
        // unable to acquire lock...
      } finally {
        optional($lock)->release();
      }
      Cache::lock('foo', 10)->block(5, function () {
        // lock acquired after waiting maximum of 5 seconds...
      });
    // --- acquire a lock in one process and release it in another process
    // for example, during a web request and release at the end of a queued job
    // that is triggered by that request
    // pass the lock scoped "owner token" to the queued job
    // so that the job can re-instantiate the lock using the given token
    // ... within controller ...
    $podcast = Podcast::find($id);
    $lock = Cache::lock('foo', 120);
    if ($result = $lock->get()) {
      ProcessPodcast::dispatch($podcast, $lock->owner());
    }
    // ... within ProcessPodcast job...
    Cache::restoreLock('foo', $this->owner)->release();
    // release a lock without respecting its current owner
    Cache::lock('foo')->forceRelease();
  

cache helper, global cache() function


    // use the Cache::shouldReceive method just as if you were testing a facade
    $value = cache('key');
    cache(['key' => 'value'], $seconds);
    cache(['key' => 'value'], now()->addMinutes(10));
    // cache function call without any arguments
    // returns instance of Illuminate\Contracts\Cache\Factory implementation
    // allows other caching methods call
    cache()->remember('users', $seconds, function () {
      return DB::table('users')->get();
    });
  

cache tags


    // not supported when using the file or database cache drivers !!!
    // when using multiple tags with caches that are stored "forever",
    // performance will be best with a driver such as "memcached",
    // which automatically purges stale records.
    // --- store
    Cache::tags(['people', 'artists'])->put('John', $john, $seconds);
    Cache::tags(['people', 'authors'])->put('Anne', $anne, $seconds);
    // --- access
    $john = Cache::tags(['people', 'artists'])->get('John');
    $anne = Cache::tags(['people', 'authors'])->get('Anne');
    // --- remove
    Cache::tags(['people', 'authors'])->flush();
    // remove only caches tagged with authors
    Cache::tags('authors')->flush();
  

adding custom cache drivers


    // --- writing the driver
    // implement the Illuminate\Contracts\Cache\Store contract
    // MongoDB cache implementation would look something like this:
    namespace App\Extensions;
    use Illuminate\Contracts\Cache\Store;
    class MongoStore implements Store {
      public function get($key) {}
      public function many(array $keys);
      public function put($key, $value, $seconds) {}
      public function putMany(array $values, $seconds);
      public function increment($key, $value = 1) {}
      public function decrement($key, $value = 1) {}
      public function forever($key, $value) {}
      public function forget($key) {}
      public function flush() {}
      public function getPrefix() {}
    }

    // --- implement each of these methods using a MongoDB connection
    for an example look at the Illuminate\Cache\MemcachedStore

    // --- driver registration
    // could be done in the "boot" method of the default App\Providers\AppServiceProvider
      Cache::extend('mongo', function ($app) {
        return Cache::repository(new MongoStore);
      });
    // OR you may create own service provider to house the extension
    // dont forget to register the provider in the config/app.php provider array
    App\Providers;
    use App\Extensions\MongoStore;
    use Illuminate\Support\Facades\Cache;
    use Illuminate\Support\ServiceProvider;
    class CacheServiceProvider extends ServiceProvider {
      // register bindings in the container
      public function register() { /* ... */ }
      // bootstrap any application services
      public function boot() {
        Cache::extend(
          'mongo', // name of the driver, correspond to driver option in config/cache.php
          // Closure that returns an Illuminate\Cache\Repository instance
          // will be passed an $app instance, which is an instance of the service container
          function ($app) {
            return Cache::repository(new MongoStore);
        });
      }
    }

    // update config/cache.php configuration file driver option to the name of extension
  

events


    // https://laravel.com/docs/5.8/events
    // execute code on every cache operation,
    // listen for the events fired by the cache
    // place event listeners within EventServiceProvider

    // event listener mappings for the application
    protected $listen = [
      'Illuminate\Cache\Events\CacheHit' => [
        'App\Listeners\LogCacheHit',
      ],
      'Illuminate\Cache\Events\CacheMissed' => [
        'App\Listeners\LogCacheMissed',
      ],
      'Illuminate\Cache\Events\KeyForgotten' => [
        'App\Listeners\LogKeyForgotten',
      ],
      'Illuminate\Cache\Events\KeyWritten' => [
        'App\Listeners\LogKeyWritten',
      ],
    ];
  

Memcached


    // configure Memcached in Lumen Api Framework
    // 1 - enable memcache in .env file:
    CACHE_DRIVER=memcached
    // 2 - change configuration option in cache config file:
    // copy cache.php file
    // from lumen_project\vendor\vendor\laravel\lumen-framework\config\
    // and put into lumen_project\config\ folder.
    // if config folder is not present in Lumen root, create and put cache.php file.
    // change value of default cache driver to memcached in config\cache.php file
    'default' => env('CACHE_DRIVER', 'memcached'),
    // 3 - install Memcached server and PHP extension (yum)
    yum -y install memcached
    yum install php-pecl-memcached
    // 5 - start Memcached server service
    service php-fpm restart
    service memcached restart
    service nginx restart
    // check with phpinfo(), then use ...

    // list all of Memcached servers in the config/cache.php
    'memcached' => [
      [
        'host' => '127.0.0.1',
        'port' => 11211,
        'weight' => 100
      ],
    ],
    // set the host option to a UNIX socket path
    // the port option should be set to 0
    'memcached' => [
      [
        'host' => '/var/run/memcached/memcached.sock',
        'port' => 0,
        'weight' => 100
      ],
    ],

    // install on WAMP
    Needed Files
      memcached.exe Direct Link
      MSVCP71.DLL Windows DLL Files
      msvcr71.dll
      php_memcache.dll Working memcache for PHP 5.3.4 OR REF
    Steps
      Copy MSVCP71.DLL, msvcr71.dll to C:\windows\sysWOW64
      Copy memcached.exe into C:\memcached
      Click Windows-Key
      Type: CMD
      press: Ctrl-Shift-Enter
      Choose yes
      type: C:\memcached\memcached.exe -d install
      type: C:\memcached\memcached.exe -d start
      Copy php_memcache.dll to C:\wamp\bin\php\php5.3.4\ext
      Restart Apache using Wamp controls
      Enable WAMP -> PHP -> PHP Extensions -> php_memcache
  

Database


    // uncomment $app->withFacades() in bootstrap/app.php
    // to access the database connection via the DB facade
    $results = DB::select("SELECT * FROM users");
    // otherwise via the app helper:
    $results = app('db')->select("SELECT * FROM users");

    // Laravel supports single database "URL"
    // as an alternative to configuring DB with multiple options:
    // mysql://root:password@127.0.0.1/forge?charset=UTF-8
    // driver://username:password@host:port/database?options

    // read & write connections
    // one DB connection for SELECT statements,
    // and another for INSERT, UPDATE, and DELETE statements
    'mysql' => [
      // override the values from the main array
      'read' => [
        'host' => [
          '192.168.1.1',
          '196.168.1.2',
        ],
      ],
      'write' => [
        'host' => [
          '196.168.1.3',
        ],
      ],
      // shared credentials, prefix, character set,...
      'driver'    => 'mysql',
      'database'  => 'database',
      'username'  => 'root',
      'password'  => '',
      'charset'   => 'utf8mb4',
      'collation' => 'utf8mb4_unicode_ci',
      'prefix'    => '',
      // optional value - allow immediate reading of records
      // that have been written to the DB during current request cycle
      'sticky'    => true,
    ],

    // --- multiple DB connections
    // access each connections listed in config/database.php
    // via the connection method on the DB facade
    $users = DB::connection('foo')->select(...);
    // access raw, underlying PDO instance
    $pdo = DB::connection()->getPdo();
  
raw sql queries

    // DB facade provides methods for each type of query:
    // select, update, insert, delete, and statement
    namespace App\Http\Controllers;
    use Illuminate\Support\Facades\DB;
    use App\Http\Controllers\Controller;
    class UserController extends Controller {
      // show a list of all of the application users
      public function index() {
        $users = DB::select('select * from users where active = ?', [1]);
        return view('user.index', ['users' => $users]);
      }
    }
    // "select" method always return an array of results
    // each result within the array will be a PHP stdClass object,
    // allowing you to access the values of the results:
    foreach ($users as $user) {
      echo $user->name;
    }

    // --- named bindings
    $results = DB::select('select * from users where id = :id', ['id' => 1]);

    // --- INSERT
    DB::insert('insert into users (id, name) values (?, ?)', [1, 'Dayle']);

    // --- UPDATE
    $affected_rows_nr = DB::update('update users set votes = 100 where name = ?', ['John']);

    // --- DELETE
    $deleted = DB::delete('delete from users');

    // --- GENERAL STATEMENT, which do not return any value
    DB::statement('drop table users');


    // --- listen - receive each SQL query executed by application
    // useful for logging queries or debugging
    // you may register query listener in a service provider
    namespace App\Providers;
    use Illuminate\Support\Facades\DB;
    use Illuminate\Support\ServiceProvider;
    class AppServiceProvider extends ServiceProvider {
      // register any application services
      public function register() { /* ... */ }
      // bootstrap any application services
      public function boot() {
        DB::listen(function ($query) {
          // $query->sql
          // $query->bindings
          // $query->time
        });
      }
    }
  
transactions

    // run a set of operations within a database transaction
    // if an exception is thrown within the transaction Closure,
    //    transaction will automatically be rolled back
    // if the Closure executes successfully
    //    transaction will automatically be committed
    DB::transaction(function () {
      DB::table('users')->update(['votes' => 1]);
      DB::table('posts')->delete();
    }, 5); // optional - number of transaction reattempts when a deadlock occurs

    // --- manually using transactions
    DB::beginTransaction();
    // THEN
    DB::commit();
    // OR
    DB::rollBack();
  

Query Builder


    namespace App\Http\Controllers;
    use Illuminate\Support\Facades\DB;
    use App\Http\Controllers\Controller;
    class UserController extends Controller {
      // sShow a list of all of the application users
      public function index() {

        // "table" method on the DB facade to begin a query
        // returns a fluent query builder instance for the given table
        // allows chaining more constraints onto the query
        // and then finally get the results using the "get" method
        $users = DB::table('users')->get();
        // return view('user.index', ['users' => $users]);

        foreach ($users as $user) {
          // Illuminate\Support\Collection, instances of the PHP stdClass object
          // access each column value as a property of the object
          echo $user->name;
        }
        // --- first - return a single stdClass object
        $user = DB::table('users')->where('name', 'John')->first();
        echo $user->name;

        // --- value - return the value of the column directly
        $email = DB::table('users')->where('name', 'John')->value('email');

        // --- find - retrieve a single row by its id column value
        $user = DB::table('users')->find(3);

        // --- pluck - retrieve a Collection containing the values of a single column
        $titles = DB::table('roles')->pluck('title');
        foreach ($titles as $title) { echo $title; }
        // specify a custom key column for the returned Collection
        $roles = DB::table('roles')->pluck('title', 'name');
        foreach ($roles as $name => $title) { echo $title; }

        // --- chunk - retrieves a small chunk of the results at a time
        // and feeds each chunk into a Closure for processing.
        // useful for writing Artisan commands that process thousands of records.
        // work with the entire users table in chunks of 100 records at a time:
        DB::table('users')->orderBy('id')->chunk(100, function ($users) {
          // keep processing the records...
          foreach ($users as $user) {
            //...
          }
          // OR stop further chunks
          return false;
        });

        // --- chunkById - when updating records while chunking
        // will automatically paginate the results based on the record primary key
        DB::table('users')->where('active', false)
          ->chunkById(100, function ($users) {
            foreach ($users as $user) {
              DB::table('users')
                ->where('id', $user->id)
                ->update(['active' => true]);
            }
          });
        // AVOID updating or deleting records inside the chunk callback !

        // --- aggregate with count, max, min, avg, sum
        // call any of these methods after constructing query
        $users = DB::table('users')->count();
        $price = DB::table('orders')->max('price');
        $price = DB::table('orders')
          ->where('finalized', 1)
          ->avg('price'); // combine with other clauses

        // --- exists , doesntExist - to determine if any records exist
        // instead of using the count method
        return DB::table('orders')->where('finalized', 1)->exists();
        return DB::table('orders')->where('finalized', 1)->doesntExist();

      }
    }
  
SELECT

    // select - define required columns
    $users = DB::table('users')
      ->select('name', 'email as user_email')
      ->get();

    // --- addSelect - add a column to existing query builder select clause
    $query = DB::table('users')->select('name');
    $users = $query->addSelect('age')->get();

    // --- distinct - force the query to return distinct results
    $users = DB::table('users')
      ->distinct()
      ->get();

    // --- count - count the total
    $posts = DB::table('users')->count();

    // select all destinations
    // and the name of the flight that most recently arrived at that destination, using a single query:
    return Destination::addSelect(['last_flight' => Flight::select('name')
        ->whereColumn('destination_id', 'destinations.id')
        ->orderBy('arrived_at', 'desc')
        ->limit(1)
    ])->get();
  
Raw Expressions

    // DB::raw - injected into the query as strings, careful, to avoid SQL injection
    $users = DB::table('users')
      ->select(DB::raw('count(*) as user_count, status'))
      ->where('status', '<>', 1)
      ->groupBy('status')
      ->get();

    // --- selectRaw - used in place of select(DB::raw(...))
    // accepts an optional array of bindings as its second argument
    $orders = DB::table('orders')
      ->selectRaw('price * ? as price_with_tax', [1.0825])
      ->get();

    // --- whereRaw , orWhereRaw - inject a raw WHERE clause into query
    $orders = DB::table('orders')
      ->whereRaw('price > IF(state = "TX", ?, 100)', [200])
      ->get();

    // --- havingRaw , orHavingRaw - set a raw string as the value of the having clause
    $orders = DB::table('orders')
      ->select('department', DB::raw('SUM(price) as total_sales'))
      ->groupBy('department')
      ->havingRaw('SUM(price) > ?', [2500])
      ->get();

    // --- orderByRaw - set a raw string as the value of the order by clause
    $orders = DB::table('orders')
      ->orderByRaw('updated_at - created_at DESC')
      ->get();
  
JOIN

    // --- join - basic "inner join"
    $users = DB::table('users')
      ->join('contacts', 'users.id', '=', 'contacts.user_id')
      ->join('orders', 'users.id', '=', 'orders.user_id')
      ->select('users.*', 'contacts.phone', 'orders.price')
      ->get();

    // --- leftJoin , rightJoin - same signature as the join
    $users = DB::table('users')
      ->leftJoin('posts', 'users.id', '=', 'posts.user_id')
      ->get();
    $users = DB::table('users')
      ->rightJoin('posts', 'users.id', '=', 'posts.user_id')
      ->get();

    // --- crossJoin - cartesian product between first and joined table
    $users = DB::table('sizes')
      ->crossJoin('colours')
      ->get();

    // --- specify constraints on the join clause inside Closure
    DB::table('users')
      ->join('contacts', function ($join) {
        $join->on('users.id', '=', 'contacts.user_id')
        ->orOn(...)
        // --- where , orWhere - compare the column against a value
        ->where('contacts.user_id', '>', 5)
        ;
      })
      ->get();

    // --- joinSub , leftJoinSub , rightJoinSub
    // join a query to a sub-query, receive three arguments:
    // sub-query, its table alias, Closure that defines the related columns
    $latestPosts = DB::table('posts')
      ->select('user_id', DB::raw('MAX(created_at) as last_post_created_at'))
      ->where('is_published', true)
      ->groupBy('user_id');
    $users = DB::table('users')
      ->joinSub($latestPosts, 'latest_posts', function ($join) {
        $join->on('users.id', '=', 'latest_posts.user_id');
      })->get();
  
UNION

    // --- union - union with a second query
    $first = DB::table('users')
      ->whereNull('first_name');

    $users = DB::table('users')
      ->whereNull('last_name')
      ->union($first)
      ->get();
    // --- unionAll - is also available and has the same method signature
  
WHERE

    $users = DB::table('users')
      ->where(
        'votes',  // name of the column
        '=',      // operator
        100       // value to evaluate against the column
      )->get();

    ->where('votes', '>=', 100)
    ->where('votes', '<>', 100)
    ->where('name', 'like', 'T%')

    // verify if a column is equal to a given value
    // pass the value directly as the second argument
    ->where('votes', 100)

    // --- array of conditions
    $users = DB::table('users')->where([
      ['status', '=', '1'],
      ['subscribed', '<>', '1'],
    ])->get();

    // --- orWhere - chain where constraints together
    ->where('votes', '>', 100)->orWhere('name', 'John')
    ->where('a', 1)->orWhere(['b' => 2, 'c' => 3]);

    // --- whereBetween , orWhereBetween
    ->whereBetween('votes', [1, 100])

    // --- whereNotBetween , orWhereNotBetween
    ->whereNotBetween('votes', [1, 100])

    // --- whereIn , whereNotIn , orWhereIn , orWhereNotIn
    ->whereIn('id', [1, 2, 3])
    ->whereNotIn('id', [1, 2, 3])

    // --- whereNull , whereNotNull , orWhereNull , orWhereNotNull
    ->whereNull('updated_at')
    ->whereNotNull('updated_at')
    ->whereNull(['id', 'expires_at']);
    ->whereNotNull(['id', 'expires_at']);

    // --- whereDate , whereMonth , whereDay , whereYear , whereTime
    ->whereDate('created_at', '2016-12-31')
    ->whereMonth('created_at', '12')
    ->whereDay('created_at', '31')
    ->whereYear('created_at', '2016')
    ->whereTime('created_at', '=', '11:20:45')

    // --- PARAMETER GROUPING
    // select * from users
    // where name = 'John' and (votes > 100 or title = 'Admin')
    DB::table('users')
      ->where('name', '=', 'John')
      ->where(function ($query) {
          $query->where('votes', '>', 100)
                ->orWhere('title', '=', 'Admin');
    })->get();
    // WHERE (gender = 'Male' and age >= 18) or (gender = 'Female' and age >= 65)
    $q->where(function ($query) {
      $query->where('gender', 'Male')
            ->where('age', '>=', 18);
    })->orWhere(function($query) {
      $query->where('gender', 'Female')
            ->where('age', '>=', 65);
    })

    // --- whereExists
    DB::table('users')
      ->whereExists(function ($query) {
        $query->select(DB::raw(1))
              ->from('orders')
              // ->whereRaw('orders.user_id = users.id');
              ->whereColumn('orders.user_id', 'users.id');
    })->get();
    // select * from users
    // where exists (
    //   select 1 from orders
    //   where orders.user_id = users.id
    // )

    // --- query a JSON column "->" operator
    // MySQL 5.7, PostgreSQL, SQL Server 2016, SQLite 3.9.0 (JSON1 extension)
    ->where('options->language', 'en')
    ->where('preferences->dining->meal', 'salad')

    // --- whereJsonContains - query JSON arrays, not supported on SQLite
    ->whereJsonContains('options->languages', 'en')
    // with multiple values, in MySQL and PostgreSQL
    ->whereJsonContains('options->languages', ['en', 'de'])

    // --- whereJsonLength - query JSON arrays by their length
    ->whereJsonLength('options->languages', 0)
    ->whereJsonLength('options->languages', '>', 1)

    // --- subquery where clauses
    // compares the results of a subquery to a given value
    $users = User::where(function ($query) {
      $query->select('type')
        ->from('membership')
        // all users who have a recent membership of a given type
        ->whereColumn('membership.user_id', 'users.id')
        ->orderByDesc('membership.start_date')
        ->limit(1);
    }, 'Pro')->get();
    // OR, compare a column to the results of a subquery
    $incomes = Income::where('amount', '<', function ($query) {
        // all income records where the amount is less than average
        $query->selectRaw('avg(i.amount)')->from('incomes as i');
    })->get();
  
Ordering , Grouping , Limit & Offset

    // --- orderBy
    ->orderBy('name', 'desc')

    // --- latest / oldest
    // order results by date
    // by default, result will be ordered by the created_at column, or pass the column name
    ->latest()->first()

    // --- inRandomOrder - sort the query results randomly
    // fetch a random user:
    ->inRandomOrder()->first()

    // --- groupBy , having - group the query results
    // having method signature is similar to that of the where method
    ->groupBy('account_id')->having('account_id', '>', 100);
    // pass multiple arguments to the groupBy method to group by multiple columns
    ->groupBy('first_name', 'status')->having('account_id', '>', 100);
    // see the havingRaw method

    // --- limit query results
    // skip , take
    ->skip(10)->take(5)
    // limit , offset - alternative
    ->offset(10)->limit(5)

    // sort all destinations based on when the last flight arrived at that destination
    // executing a single query
    return Destination::orderByDesc(
      Flight::select('arrived_at')
        ->whereColumn('destination_id', 'destinations.id')
        ->orderBy('arrived_at', 'desc')
        ->limit(1)
    )->get();
  
Conditional Clauses

    // apply to a query only when something else is true.
    // apply a where statement if a given input value is presentin request
    $role = $request->input('role');
    $users = DB::table('users')
      ->when($role, function ($query, $role) {
        return $query->where('role_id', $role);
      })->get();
    // "when" method Closure only executes when the first parameter is true.
    // another Closure passed as the third parameter to the when method
    // is executed if the first parameter evaluates as false
    $sortBy = null;
    $users = DB::table('users')
      ->when($sortBy, function ($query, $sortBy) {
          return $query->orderBy($sortBy);
      }, function ($query) {
          return $query->orderBy('name');
      })->get();
  
INSERT

    // --- insert - inserting records into the database table
    // accepts an array of column names and values
    DB::table('users')->insert(
      ['email' => 'john@example.com', 'votes' => 0]
    );
    // insert several records into the table
    DB::table('users')->insert([
      ['email' => 'taylor@example.com', 'votes' => 0],
      ['email' => 'dayle@example.com', 'votes' => 0]
    ]);

    // --- insertGetId - auto-incrementing IDs (if table has)
    // insert a record and then retrieve the ID
    $id = DB::table('users')->insertGetId(
      ['email' => 'john@example.com', 'votes' => 0]
    );
    // in PostgreSQL, method expects the auto-incrementing column to be named "id"
    // if you would like to retrieve the ID from a different "sequence"
    // pass the column name as the second parameter to the insertGetId method
  
UPDATE

    // like the "insert" method, accepts an array of column and value pairs
    // containing the columns to be updated
    DB::table('users')
      ->where('id', 1)
      ->update(['votes' => 1]);

    // --- updateOrInsert
    // update an existing record in the database or create it if no matching record exists
    // accepts two arguments:
    // an array of conditions by which to find the record,
    // and an array of column and value pairs containing the columns to be updated
    // 1 - attempt to locate a matching database record using the first argument column and value pairs
    //     and update with the values in the second argument
    // 2 - OR new record will be inserted with the merged attributes of both arguments
    DB::table('users')
      ->updateOrInsert(
        ['email' => 'john@example.com', 'name' => 'John'],
        ['votes' => '2']
      );

    // --- updating JSON Columns , only supported on MySQL 5.7+
    // "->" - to access the appropriate key in the JSON object
    // "."  - when updating a JSON column
    DB::table('users')
      ->where('id', 1)
      ->update(['options->enabled' => true]);

    // --- increment , decrement
    ->increment('votes');
    ->increment('votes', 5);
    ->decrement('votes');
    ->decrement('votes', 5);
    // specify additional columns to update during the operation:
    ->increment('votes', 1, ['name' => 'John']);
  
DELETE

    DB::table('users')->delete();
    DB::table('users')->where('votes', '>', 100)->delete();
    // truncate the entire table
    // remove all rows and reset the auto-incrementing ID to zero
    DB::table('users')->truncate();
  
Pessimistic Locking

    // --- sharedLock
    // prevents selected rows from being modified until transaction commits
    DB::table('users')
      ->where('votes', '>', 100)
      ->sharedLock()->get();

    // --- lockForUpdate
    // prevents rows modification or selection with another shared lock
    DB::table('users')->where('votes', '>', 100)->lockForUpdate()->get();
  
Debugging

    // --- dd - display the debug information and stop executing the request
    DB::table('users')->where('votes', '>', 100)->dd();
    // --- dump - display debug info but allow the request to keep executing:
    DB::table('users')->where('votes', '>', 100)->dump();
    // --- toSql
    DB::table('users')->where('votes', '>', 100)->toSql();
    // --- getBindings
  

Pagination


    // --- paginate - sets the proper limit and offset based on the current page
    // the current page is detected by HTTP request query string argument
    // this value is automatically detected and inserted into generated links
    namespace App\Http\Controllers;
    use Illuminate\Support\Facades\DB;
    use App\Http\Controllers\Controller;
    class UserController extends Controller {
      public function index() {
        $users = DB::table('users')->paginate(15);
        return view('user.index', ['users' => $users]);
      }
    }

    // --- simplePaginate - display simple "Next" and "Previous" links
    // useful for large datasets
    $users = DB::table('users')->simplePaginate(15);
  

Eloquent


    $users = App\User::paginate(15);
    $users = User::where('votes', '>', 100)->paginate(15);
    $users = User::where('votes', '>', 100)->simplePaginate(15);

    // --- create a pagination instance manually, passing it an array of items
    // array_slice the array of results you pass to the paginator

    // --- Illuminate\Pagination\Paginator (corresponds to the "simplePaginate")
    // does not need to know the total number of items in the result set
    // does not have methods for retrieving the index of the last page

    // --- Illuminate\Pagination\LengthAwarePaginator
    // corresponds to the "paginate"
    // accepts almost the same arguments as the Paginator
    // does require a count of the total number of items in the result set
  

Displaying


    // --- in Blade template
    <div class="container">
      @foreach ($users as $user)
        {{ $user->name }}
      @endforeach
    </div>
    {{ $users->links() }}

    // --- withPath - customize generating links URI
    Route::get('users', function () {
      $users = App\User::paginate(15);
      // http://example.com/custom/url?page=N
      $users->withPath('custom/url');
      // ...
    });

    // --- appends - append to the query string of pagination links
    // append sort=votes:
    {{ $users->appends(['sort' => 'votes'])->links() }}

    // --- fragment - append a "hash fragment"
    // append #foo:
    {{ $users->fragment('foo')->links() }}

    // --- onEachSide - control how many additional links are displayed
    // by default, three links are displayed on each side of the primary paginator links
    {{ $users->onEachSide(5)->links() }}

    // --- Converting Results To JSON
    // paginator result classes implement the Illuminate\Contracts\Support\Jsonable
    // convert a paginator instance to JSON from a route or controller action:
    Route::get('users', function () {
      return App\User::paginate();
    });
    // example of the JSON created by returning a paginator instance from a route
    {
      "total": 50,
      "per_page": 15,
      "current_page": 1,
      "last_page": 4,
      "first_page_url": "http://laravel.app?page=1",
      "last_page_url": "http://laravel.app?page=4",
      "next_page_url": "http://laravel.app?page=2",
      "prev_page_url": null,
      "path": "http://laravel.app",
      "from": 1,
      "to": 15,
      "data":[
        {
          // Result Object
        },
        {
          // Result Object
        }
      ]
    }
  

Customizing The Pagination View


      // define own views to render links
      // call links method on a paginator instance with first argument
      {{ $paginator->links('view.name') }}
      // passing data to the view...
      {{ $paginator->links('view.name', ['foo' => 'bar']) }}

      // OR customize pagination views with export to resources/views/vendor directory
      php artisan vendor:publish --tag=laravel-pagination
      // bootstrap-4.blade.php file corresponds to the default pagination view
      // edit to modify the pagination HTML
      // to designate a different file as the default pagination view,
      // use the paginator defaultView and defaultSimpleView within AppServiceProvider
      use Illuminate\Pagination\Paginator;
      public function boot() {
        Paginator::defaultView('view-name');
        Paginator::defaultSimpleView('view-name');
        Paginator::useBootstrap(); // keep using bootstrap (Laravel 8+)
      }
  

Paginator Instance Methods

Method Description
$results->count() Get the number of items for the current page
$results->currentPage() Get the current page number
$results->firstItem() Get the result number of the first item in the results
$results->getOptions() Get the paginator options
$results->getUrlRange($start, $end) Create a range of pagination URLs
$results->hasMorePages() Determine if there are enough items to split into multiple pages
$results->lastItem() Get the result number of the last item in the results
$results->lastPage() Get the page number of the last available page (Not available when using simplePaginate)
$results->nextPageUrl() Get the URL for the next page
$results->onFirstPage() Determine if the paginator is on the first page
$results->perPage() The number of items to be shown per page
$results->previousPageUrl() Get the URL for the previous page
$results->total() Determine the total number of matching items in the data store (Not available when using simplePaginate)
$results->url($page) Get the URL for a given page number
paginate / simplePaginate
database table has only few rows and does not grow large paginate / simplePaginate
database table has so many rows and grows quickly simplePaginate
it is mandatory to provide the user option to jump to specific pages paginate
it is mandatory to show the user total no of results paginate
not actively using pagination links simplePaginate
UI/UX does not affect from switching numbered pagination links to next / previous pagination links simplePaginate
Using "load more" button or "infinite scrolling" for pagination simplePaginate

Migrations


    use Illuminate\Support\Facades\Schema;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Database\Migrations\Migration;
    class CreateFlightsTable extends Migration {
      // run the migrations
      public function up() {
        Schema::create('flights', function (Blueprint $table) {
          $table->bigIncrements('id');
          $table->string('name');
          $table->string('airline');
          $table->timestamps();
        });
      }
      // reverse the migrations
      public function down() {
        Schema::drop('flights');
      }
    }
  
Tables

    Schema::create('users', function (Blueprint $table) {
      $table->bigIncrements('id');
    });

    if (Schema::hasTable('users')) {
      // ...
    }

    if (Schema::hasColumn('users', 'email')) {
      // ...
    }

    Schema::connection('db_connection_name')
      ->create('users', function (Blueprint $table) {
        $table->bigIncrements('id');
      });

    $table->engine = 'InnoDB';// MySQL
    $table->charset = 'utf8'; // MySQL
    $table->collation = 'utf8_unicode_ci'; // MySQL
    $table->temporary(); // except SQL Server

    Schema::rename($from, $to);
    Schema::drop('users');
    Schema::dropIfExists('users');
    // before renaming a table,
    // verify that any foreign key constraints on the table
    // have an explicit name in migration files
    // instead of letting Laravel assign a convention based name
    // otherwise, the foreign key constraint name will refer to the old table name
  
Columns

    Schema::table('users', function (Blueprint $table) {
      $table->string('email');
    });

    // --- COLUMN TYPES

    // auto-incrementing (primary key)
    ->increments('id') // UNSIGNED INTEGER
    ->bigIncrements('id') // UNSIGNED BIGINT
    ->mediumIncrements('id') // UNSIGNED MEDIUMINT
    ->smallIncrements('id') // UNSIGNED SMALLINT
    ->tinyIncrements('id') // UNSIGNED TINYINT

    ->integer('votes') // INTEGER
    ->bigInteger('votes') // BIGINT
    ->mediumInteger('votes') // MEDIUMINT
    ->smallInteger('votes') // SMALLINT
    ->tinyInteger('votes') // TINYINT
    ->unsignedInteger('votes') // UNSIGNED INTEGER
    ->unsignedBigInteger('votes') // UNSIGNED BIGINT
    ->unsignedMediumInteger('votes') // UNSIGNED MEDIUMINT
    ->unsignedSmallInteger('votes') // UNSIGNED SMALLINT
    ->unsignedTinyInteger('votes') // UNSIGNED TINYINT

    ->decimal('amount', 8, 2) // DECIMAL, precision (total digits) and scale (decimal digits)
    ->unsignedDecimal('amount', 8, 2) // UNSIGNED DECIMAL, precision (total digits) and scale (decimal digits)
    ->double('amount', 8, 2) // DOUBLE, precision (total digits) and scale (decimal digits)
    ->float('amount', 8, 2) // FLOAT, precision (total digits) and scale (decimal digits)

    ->char('name', 100) // CHAR with an optional length
    ->string('name', 100) // VARCHAR with a optional length
    ->lineString('positions') // LINESTRING
    ->multiLineString('positions') // MULTILINESTRING

    ->text('description') // TEXT
    ->longText('description') // LONGTEXT
    ->mediumText('description') // MEDIUMTEXT
    ->boolean('confirmed') // BOOLEAN

    ->year('birth_year') // YEAR
    ->date('created_at') // DATE
    ->dateTime('created_at') // DATETIME
    ->dateTimeTz('created_at') // DATETIME (with timezone)
    ->time('sunrise') // TIME
    ->timeTz('sunrise') // TIME (with timezone)

    ->timestamps() // nullable created_at and updated_at TIMESTAMPs
    ->nullableTimestamps() // alias of timestamps() method
    ->timestampsTz() // nullable created_at and updated_at TIMESTAMP (with timezone)s
    ->timestamp('added_on') // TIMESTAMP
    ->timestampTz('added_on') // TIMESTAMP (with timezone)
    ->softDeletes() // nullable deleted_at TIMESTAMP for soft deletes
    ->softDeletesTz() // nullable deleted_at TIMESTAMP (with timezone) for soft deletes

    ->enum('level', ['easy', 'hard']) // ENUM
    ->set('flavors', ['strawberry', 'vanilla']) // SET

    ->ipAddress('visitor') // IP address
    ->macAddress('device') // MAC address

    ->json('options') // JSON
    ->jsonb('options') // JSONB

    ->rememberToken() // nullable remember_token VARCHAR(100)
    ->uuid('id') // UUID

    ->binary('data') // BLOB

    ->geometry('positions') // GEOMETRY
    ->geometryCollection('positions') // GEOMETRYCOLLECTION
    ->morphs('taggable') // taggable_id UNSIGNED BIGINT and taggable_type VARCHARs
    ->multiPoint('positions') // MULTIPOINT
    ->multiPolygon('positions') // MULTIPOLYGON
    ->nullableMorphs('taggable') // nullable versions of morphs() columns
    ->point('position') // POINT
    ->polygon('positions') // POLYGON
  
Column Modifiers

    // add the doctrine/dbal dependency to composer.json
    // and run the composer update command in terminal to install the library

    Schema::table('users', function (Blueprint $table) {
      $table->string('email')->nullable();
    });

    ->after('column') // place the column "after" another column (MySQL)
    ->autoIncrement() // set INTEGER columns as auto-increment (primary key)
    ->charset('utf8') // specify a character set for the column (MySQL)
    ->collation('utf8_unicode_ci') // specify a collation for the column (MySQL/SQL Server)
    ->comment('my comment') // add a comment to a column (MySQL/PostgreSQL)
    ->default($value) // specify a "default" value for the column
    ->first() // place the column "first" in the table (MySQL)
    ->nullable($value = true) // allows (by default) NULL values to be inserted into the column
    ->storedAs($expression) // reate a stored generated column (MySQL)
    ->unsigned() // set INTEGER columns as UNSIGNED (MySQL)
    ->useCurrent() // set TIMESTAMP columns to use CURRENT_TIMESTAMP as default value
    ->virtualAs($expression) // create a virtual generated column (MySQL)
    ->generatedAs($expression) // create an identity column with specified sequence options (PostgreSQL)
    ->always() // defines the precedence of sequence values over input for an identity column (PostgreSQL)

    // --- UPDATING COLUMN ATTRIBUTES
    // only the following column types can be "changed":
    // integer, bigInteger, smallInteger
    // unsignedBigInteger, unsignedInteger, unsignedSmallInteger
    // date, dateTime, dateTimeTz,
    // text, longText, mediumText, string
    // decimal, binary, boolean, json, time

    // increase the size of the name column from 25 to 50
    Schema::table('users', function (Blueprint $table) {
      $table->string('name', 50)->change();
    });
    // modify a column to be nullable:
    Schema::table('users', function (Blueprint $table) {
      $table->string('name', 50)->nullable()->change();
    });

    // --- RENAME
    // table that also has a column of type enum is not currently supported
    Schema::table('users', function (Blueprint $table) {
      $table->renameColumn('from', 'to');
    });

    // --- DROP
    // before dropping columns from a SQLite database
    // add the doctrine/dbal dependency to composer.json
    // and run the composer update command in terminal to install the library
    Schema::table('users', function (Blueprint $table) {
      $table->dropColumn('votes');
    });
    // drop multiple columns from a table
    Schema::table('users', function (Blueprint $table) {
        $table->dropColumn(['votes', 'avatar', 'location']);
    });
    // dropping or modifying multiple columns within a single migration
    // while using a SQLite database is not supported

    ->dropMorphs('morphable') // drop the morphable_id and morphable_type columns
    ->dropRememberToken() // drop the remember_token column
    ->dropSoftDeletes() // drop the deleted_at column
    ->dropSoftDeletesTz() // Alias of dropSoftDeletes() method
    ->dropTimestamps() // drop the created_at and updated_at columns
    ->dropTimestampsTz() // alias of dropTimestamps() method
  
Indexes

    // concatenate table name, name of the indexed column and index type
    // users_id_primary, users_email_unique, geo_state_index, geo_location_spatialindex

    // chain onto the column definition
    $table->string('email')->unique();
    // create the index after defining the column
    $table->unique('email');
    // compound (or composite) index
    $table->index(['account_id', 'created_at']);
    // pass a second argument to specify index name
    $table->unique('email', 'unique_email');

    // each accepts an optional second argument to specify the name of the index
    // otherwise name will be derived from the names of the table and column(s)
    ->primary('id') // primary key
    ->primary(['id', 'parent_id']) // composite keys
    ->unique('email') // unique index
    ->index('state') // plain index
    ->spatialIndex('location') // spatial index (except SQLite)

    // --- RENAME
    // accepts current index name and desired name
    $table->renameIndex('from', 'to')

    // --- DROP
    // concatenate table name, name of the indexed column and index type
    ->dropPrimary('users_id_primary') // drop a primary key from the "users" table
    ->dropUnique('users_email_unique') // drop a unique index from the "users" table
    ->dropIndex('geo_state_index') // drop a basic index from the "geo" table
    // drop a spatial index from the "geo" table (except SQLite)
    ->dropSpatialIndex('geo_location_spatialindex')
    // pass an array of columns into a method that drops indexes,
    // conventional index name will be generated: table name, columns and key type
    Schema::table('geo', function (Blueprint $table) {
      $table->dropIndex(['state']); // Drops index 'geo_state_index'
    });

    // foreign key constraints
    Schema::table('posts', function (Blueprint $table) {
      // user_id column on the posts table
      $table->unsignedBigInteger('user_id');
      // references the id column on a users table
      $table->foreign('user_id')->references('id')->on('users');
    });
    // specify action for the "on delete" and "on update" properties of the constraint
    $table->foreign('user_id')
          ->references('id')->on('users')
          ->onDelete('cascade');
    // foreign key constraints use the same naming convention as indexes
    // table name and columns then suffix the name with "_foreign":
    $table->dropForeign('posts_user_id_foreign');
    // OR pass an array value for automatic conventional constraint name when dropping
    $table->dropForeign(['user_id']);
    // enable or disable foreign key constraints within migrations
    Schema::enableForeignKeyConstraints();
    Schema::disableForeignKeyConstraints();

    // SQLite disables foreign key constraints by default
    // enable foreign key support in database configuration
    // before attempting to create them in migrations
  
utf8mb4 character set (emojis)

    // Laravel uses the utf8mb4 character set by default,
    // which includes support for storing "emojis" in the database
    // MySQL version older than the 5.7.7 or MariaDB older than the 10.2.2
    // may need to manually configure the default string length generated by migrations
    // call Schema::defaultStringLength method within AppServiceProvider:
    use Illuminate\Support\Facades\Schema;
    // ...
      public function boot() {
          Schema::defaultStringLength(191);
      }
    // ...
    // alternatively, enable innodb_large_prefix option for database
  

Database Seeding


    // modify the default DatabaseSeeder class
    // and add a database insert statement to the run method
    namespace Database\Seeders;
    use Illuminate\Support\Str;
    use Illuminate\Database\Seeder;
    use Illuminate\Support\Facades\DB;
    class DatabaseSeeder extends Seeder {
      // run the database seeds
      public function run() {

        DB::table('users')->insert([
          'name' => Str::random(10),
          'email' => Str::random(10).'@gmail.com',
          'password' => bcrypt('secret'),
        ]);

        // Laravel <=7, using factory, create 50 users and attach a relationship to each user
        factory(App\User::class, 50)->create()->each(function ($user) {
          $user->posts()->save(factory(App\Post::class)->make());
        });

        // Laravel 8+, using factory, create 50 users and attach a relationship to each user
        User::factory()
          ->count(50)
          ->hasPosts(1)
          ->create();

        // execute additional seed classes
        $this->call([
          UsersTableSeeder::class,
          PostsTableSeeder::class,
          CommentsTableSeeder::class,
        ]);

      }
    }
  

Redis


    // config/database.php

    'redis' => [
      'client' => 'predis',
      'default' => [
        // 'scheme' => 'tls', // default is tcp
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD', null),
        'port' => env('REDIS_PORT', 6379),
        'database' => env('REDIS_DB', 0),
      ],
      'cache' => [
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD', null),
        'port' => env('REDIS_PORT', 6379),
        'database' => env('REDIS_CACHE_DB', 1),
      ],
    ],
    // OR
    'redis' => [
      'client' => env('REDIS_CLIENT', 'phpredis'),
      'default' => [
          'url' => 'tcp://127.0.0.1:6379?database=0',
      ],
      'cache' => [
          'url' => 'tls://user:password@127.0.0.1:6380?database=1',
      ],
    ],

    // --- cluster of Redis servers
    'redis' => [
      'client' => env('REDIS_CLIENT', 'phpredis'),
      'clusters' => [
        'default' => [
          [
            'host' => env('REDIS_HOST', 'localhost'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', 6379),
            'database' => 0,
          ],
        ],
      ],
    ],
    // client-side sharding does not handle failover
    // primarily suited for cached data available from another primary data store
    // use native Redis clustering
    'redis' => [
      'client' => env('REDIS_CLIENT', 'phpredis'),
      'options' => [
        'cluster' => 'redis',
      ],
      'clusters' => [
        // ...
      ],
    ],

    // --- Predis
    // supports additional connection parameters defined for each Redis servers
    // https://github.com/nrk/predis/wiki/Connection-Parameters
    'default' => [
      'host' => env('REDIS_HOST', 'localhost'),
      'password' => env('REDIS_PASSWORD', null),
      'port' => env('REDIS_PORT', 6379),
      'database' => 0,
      'read_write_timeout' => 60,
    ],

    // --- PhpRedis
    // change the client option of Redis configuration to phpredis
    // found in config/database.php configuration file
    'redis' => [
      'client' => 'phpredis',
      // rest of Redis configuration...
    ],
    // supports the following additional connection parameters:
    // persistent, prefix, read_timeout and timeout.
    // add any of these in config/database.php
    'default' => [
      'host' => env('REDIS_HOST', 'localhost'),
      'password' => env('REDIS_PASSWORD', null),
      'port' => env('REDIS_PORT', 6379),
      'database' => 0,
      'read_timeout' => 60,
    ],
  

Interacting


    // call Redis GET command with Redis facade
    namespace App\Http\Controllers;
    use App\Http\Controllers\Controller;
    use Illuminate\Support\Facades\Redis;
    class UserController extends Controller {
      // how the profile for the given user
      public function showProfile($id) {
        $user = Redis::get('user:profile:'.$id);
        return view('user.profile', ['user' => $user]);
      }
    }

    // --- pass commands to the Redis server
    // ...
      Redis::set('name', 'Taylor');
      $values = Redis::lrange('names', 5, 10);
    // ...
    // alternatively, use "command" method
    // first argument - name of the command
    // second argument - array of values
    $values = Redis::command('lrange', ['name', 5, 10]);

    // --- multiple redis connections
    // get a default Redis server instance
    $redis = Redis::connection();
    // pass the connection or cluster name to the connection method
    // to get a specific server or cluster as defined in Redis configuration
    $redis = Redis::connection('my-connection');

    // --- pipelining commands
    // to send many commands to the server in one operation
    // Closure as argument that receives a Redis instance
    // issue all commands to this instance and they will all be executed
    // within a single operation
    Redis::pipeline(function ($pipe) {
      for ($i = 0; $i < 1000; $i++) {
        $pipe->set("key:$i", $i);
      }
    });
  

Pub , Sub (publish and subscribe)


    // listen for messages on a given "channel"
    // publish messages to the channel from another application
    // or even using another programming language
    // communication between applications and processes

    // --- subscribe - setup a channel listener
    namespace App\Console\Commands;
    use Illuminate\Console\Command;
    use Illuminate\Support\Facades\Redis;
    class RedisSubscribe extends Command {
      // name and signature of the console command
      protected $signature = 'redis:subscribe';
      // console command description.
      protected $description = 'Subscribe to a Redis channel';
      // eExecute the console command
      public function handle() {
        Redis::subscribe(['test-channel'], function ($message) {
          echo $message;
        });
      }
    }

    // --- publish
    Route::get('publish', function () {
      // Route logic...
      Redis::publish('test-channel', json_encode(['foo' => 'bar']));
    });

    // --- psubscribe - subscribe to a wildcard channel
    // useful for catching all messages on all channels
    // $channel is passed as the second argument to the provided callback Closure
    Redis::psubscribe(['*'], function ($message, $channel) {
      echo $message;
    });
    Redis::psubscribe(['users.*'], function ($message, $channel) {
      echo $message;
    });
  

Eloquent

Conventions


    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class TestModel extends Model {

      protected $table = 'test_models'; // "snake case", plural name of the class
      protected $primaryKey = 'id'; // or override with custom like test_model_id
      // to use a non-incrementing or a non-numeric primary key:
      public $incrementing = false;
      // if primary key is not an integer:
      protected $keyType = 'string';
      // Eloquent expects created_at and updated_at columns to exist
      // if you do not wish to have these columns automatically managed:
      public $timestamps = false;
      // customize timestamps format
      // determine how date attributes are stored in the database
      // and format when the model is serialized to an array or JSON
      protected $dateFormat = 'U';
      // customize columns names used to store the timestamps
      const CREATED_AT = 'creation_date';
      const UPDATED_AT = 'last_update';
      // specify a different connection, instead of default
      protected $connection = 'connection-name';
      // define default values for some attributes
      protected $attributes = [
        'delayed' => false,
      ];
      // which fields will be Carbon-ized
      protected $dates = ['created_at', 'deleted_at'];
      // additional values returned in JSON
      protected $appends = ['field1', 'field2'];

      // mass assignable "white list"
      protected $fillable = ['name']; // then you can use "create" or "fill"
      // mass assignable "black list"
      protected $guarded = ['price'];
      // use either $fillable or $guarded - not both !!!
      protected $guarded = []; // all attributes are mass assignable

      // attributes excluded from the model JSON form
      protected $hidden = [
          'password',
          'api_token',
      ];

      protected $perPage = 25; // override pagination count (default 15)

      // override default behavior
      public static function boot() {
          parent::boot();
          static::updating(function($model) {
              // do some logging
              // override some property like $model->something = transform($something);
          });
          // setting some field value at the moment of creating the model object
          self::creating(function ($model) {
            $model->uuid = (string)Uuid::generate();
          });
      }
      // booting()/booted() - any actions required before/after model boots

    }
  
SELECT

    // --- all - return all of the results in the model table
    $flights = App\Flight::all();
    foreach ($flights as $flight) {
      echo $flight->name;
    }

    $flights = App\Flight::where('active', 1)
                ->orderBy('name', 'desc')
                ->take(10)
                ->get();

    // --- fresh - reload a fresh model instance from the database
    // existing model instance will not be affected
    $flight = App\Flight::where('number', 'FR 900')->first();
    $freshFlight = $flight->fresh();

    // --- refresh - re-hydrate the existing model using fresh data from the database
    // all of its loaded relationships will be refreshed
    $flight = App\Flight::where('number', 'FR 900')->first();
    $flight->number = 'FR 456';
    $flight->refresh();
    $flight->number; // "FR 900"

    // --- chunk - process thousands, retrieves based on offset and limit (slower)
    // use if db instance does not have enough memory
    Flight::chunk(200, function ($flights) {
      foreach ($flights as $flight) {
        // ...
      }
    });
    // --- chunkById - retrieves db results based on an primary field (faster)
    $posts = Post::chunkById(100, function($posts){
      foreach ($posts as $post){
       // Process posts
      }
    });

    // --- cursor - iterate through database records using a cursor
    // will only execute a single query
    // for processing large amounts of data, greatly reduce memory usage
    // returns instance of Illuminate\Support\LazyCollection
    // use if web app running application has less memory
    foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
      //
    }

    // --- find - model by its primary key
    $flight = App\Flight::find(1);
    // with an array of primary keys
    $flights = App\Flight::find([1, 2, 3]);

    // --- first - first model matching the query constraints
    $flight = App\Flight::where('active', 1)->first();
    // --- findOrFail , firstOrFail - throw an exception if a model is not found
    $model = App\Flight::findOrFail(1);
    $model = App\Flight::where('legs', '>', 100)->firstOrFail();
    // not necessary to write explicit checks to return 404 responses
    Route::get('/api/flights/{id}', function ($id) {
      return App\Flight::findOrFail($id);
    });
    // specify the attributes to select as the second argument
    $user = App\User::find(1, ['name', 'age']);
    $user = App\User::findOrFail(1, ['name', 'age']);

    // --- where[field]
    $users = User::whereApproved(1)->get(); // same as: User::where('approved', 1)->get();
    User::whereDate('created_at', date('Y-m-d'));
    User::whereDay('created_at', date('d'));
    User::whereMonth('created_at', date('m'));
    User::whereYear('created_at', date('Y'));

    // --- avoiding IF-ELSE
    $query = Author::query();
    $query->when(request('filter_by') == 'likes', function ($q) {
      return $q->where('likes', '>', request('likes_amount', 0));
    });
    $query->when(request('filter_by') == 'date', function ($q) {
      return $q->orderBy('created_at', request('ordering_rule', 'desc'));
    });
    // passing of the parameters
    $query->when(request('role', false), function ($q, $role) {
      return $q->where('role_id', $role);
    });

    // --- count, max, min, avg, and sum aggregates
    // return the appropriate scalar value instead of a full model instance
    $count = App\Flight::where('active', 1)->count();
    $posts = Post::count();
    $max = App\Flight::where('active', 1)->max('price');

    // --- replicate() - clone a model using
    // create a copy of the model into a new, non-existing instance
    $user = App\User::find(1);
    $newUser = $user->replicate();
    $newUser->save();
  
INSERT , UPDATE

    // --- save - create a new record
    // create a new model instance, set attributes on the model
    // created_at and updated_at timestamps will automatically be set
    ace App\Http\Controllers;
    use App\Flight;
    use Illuminate\Http\Request;
    use App\Http\Controllers\Controller;
    class FlightController extends Controller {
      public function store(Request $request) {
        // Validate the request...
        $flight = new Flight;
        $flight->name = $request->name;
        $flight->save();
      }
    }
    // also used to update models that already exist
    $flight = App\Flight::find(1);
    $flight->name = 'New Flight Name';
    $flight->save();
    // overriding default updated_at with our pre-defined one
    $product = Product::find($id);
    $product->updated_at = '2019-01-01 10:00:00';
    $product->save(['timestamps' => false]);

    // --- update - mass updates, returns affected rows count
    App\Flight::where('active', 1)
      ->where('destination', 'San Diego')
      ->update(['delayed' => 1]);
    // mass update via Eloquent will not fire
    // saving, saved, updating, and updated model events
    // models are never actually retrieved when issuing a mass update

    // Eloquent models protect against mass-assignment by default
    // specify either a "fillable" or "guarded" attribute on the model
    // allow with "protected $fillable = ['name'];" in model class
    // disallow with "protected $guarded  = ['name'];"
    // then you can use this methods:
    // --- create - save a new model in a single line
    // inserted model instance will be returned
    $flight = App\Flight::create(['name' => 'Flight 10']);
    // --- fill - populate already received model with attributes
    $flight->fill(['name' => 'Flight 22']);

    // --- firstOrCreate
    // try to locate a record matching the given attributes
    // if the model is not found,
    // a record will be inserted with the attributes from the first parameter,
    // along with those in the optional second parameter.
    // --- firstOrNew
    // try to locate a record matching the given attributes
    // if a model is not found, new model instance will be returned.
    // if returned model has not yet been persisted to the database
    // call save() manually to persist it.
    // retrieve flight by name, or create it if it doesnt exist:
    $flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);
    // retrieve flight by name
    // or create it with the name, delayed, arrival_time attributes:
    $flight = App\Flight::firstOrCreate(
      ['name' => 'Flight 10'],
      ['delayed' => 1, 'arrival_time' => '11:30']
    );
    // retrieve by name, or instantiate:
    $flight = App\Flight::firstOrNew(['name' => 'Flight 10']);
    // retrieve by name,
    // or instantiate with name, delayed, arrival_time attributes:
    $flight = App\Flight::firstOrNew(
      ['name' => 'Flight 10'],
      ['delayed' => 1, 'arrival_time' => '11:30']
    );
    // second argument array will be merged with the first argument to the method ($attributes)
    // when creating the related model if one does not already exist
    $user->roles()->firstOrCreate([
      'name' => 'Administrator',
    ], [
      'created_by' => $user->id,
    ]);

    // --- updateOrCreate
    // update an existing model or create a new model if none exists
    // call save() to persists the model.
    // if there is a flight from Oakland to San Diego, set the price to $99,
    // if no matching model exists, create one:
    $flight = App\Flight::updateOrCreate(
      ['departure' => 'Oakland', 'destination' => 'San Diego'],
      ['price' => 99, 'discounted' => 1]
    );

    // --- getOriginal
    // get the original attributes after mutating an Eloquent record
    $user = App\User::first();
    $user->name; //John
    $user->name = "Peter"; //Peter
    $user->getOriginal('name'); //John
    $user->getOriginal(); //Original $user record
    $user->getRawOriginal();

    // --- isDirty - if the model or given attribute have been modified
    $user = App\User::first();
    $user->isDirty();          //false
    $user->name = "Peter";
    $user->isDirty();          //true
    // check if a particular attribute is changed
    $user->isDirty('name');    //true
    $user->isDirty('age');     //false

    // --- getChanges - changed attributes
    // only if you save the model or sync the changes using syncChanges()
    $user->getChanges()//[ "name" => "Peter", ... ]
  
DELETE

    $flight = App\Flight::find(1);
    $flight->delete();

    // --- destroy - delete by primary key
    // accepts array or a collection of primary keys
    App\Flight::destroy(1);
    App\Flight::destroy(1, 2, 3);
    App\Flight::destroy([1, 2, 3]);
    App\Flight::destroy(collect([1, 2, 3]));

    // --- delete by query
    // will not fire any model events ("deleting", "deleted")
    $deletedRows = App\Flight::where('active', 0)->delete();
  
Soft Delete

    // not removed from database
    // deleted_at attribute is set on the model and inserted into the database
    // if a model has a non-null deleted_at value,
    // the model has been soft deleted
    // enable soft deletes for a model,
    // use the Illuminate\Database\Eloquent\SoftDeletes trait on the model
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\SoftDeletes;
    class Flight extends Model {
      use SoftDeletes;
    }
    // also add the deleted_at column to database table
    // schema builder contains a helper method
    Schema::table('flights', function (Blueprint $table) {
      $table->softDeletes();
    });
    // now, when you call the "delete" method on the model,
    // deleted_at column will be set to the current date and time
    // soft deleted models will automatically be excluded from all query results.
    // to determine if a given model instance has been soft deleted
    if ($flight->trashed()) {
      // ...
    }

    // --- withTrashed - include soft deleted models
    $flights = App\Flight::withTrashed()
      ->where('account_id', 1)
      ->get();
    // used on a relationship query
    $flight->history()->withTrashed()->get();

    // --- onlyTrashed - retrieve ONLY soft deleted models
    $flights = App\Flight::onlyTrashed()
      ->where('airline_id', 1)
      ->get();

    // --- restore - restoring soft deleted models, will not fire any model events
    $flight->restore();
    // in a query
    App\Flight::withTrashed()
      ->where('airline_id', 1)
      ->restore();
    // on relationships
    $flight->history()->restore();

    // --- forceDelete - permanently remove a soft deleted model
    // single model
    $flight->forceDelete();
    // all related models.
    $flight->history()->forceDelete();
  
Query Global Scopes

    // add constraints to all queries for a given model
    // make sure every query for a given model receives certain constraints
    // define a class that implements the Illuminate\Database\Eloquent\Scope
    // requires to "apply" and may add "where" constraints to the query
    namespace App\Scopes;
    use Illuminate\Database\Eloquent\Scope;
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\Builder;
    class AgeScope implements Scope {
      public function apply(Builder $builder, Model $model) {
        $builder->where('age', '>', 200);
      }
    }
    // if global scope is adding columns to the select clause of the query
    // use the addSelect method instead of select
    // prevent unintentional replacement of the query existing select clause

    // --- assign a global scope to a model
    // override a given model boot method and use the addGlobalScope method
    namespace App;
    use App\Scopes\AgeScope;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      protected static function booted() {
        static::addGlobalScope(new AgeScope);
      }
    }
    // then query to User::all() will produce the following SQL:
    // SELECT * FROM `users` WHERE `age` > 200

    // --- anonymous global scopes
    // do not need a separate clas
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\Builder;
    class User extends Model {
      protected static function booted() {
        static::addGlobalScope('age', function (Builder $builder) {
          $builder->where('age', '>', 200);
        });
        // always be ordered by name field
        static::addGlobalScope('order', function (Builder $builder) {
          $builder->orderBy('name', 'asc');
        });
      }
    }

    // --- removing global scopes for a quary
    User::withoutGlobalScope(AgeScope::class)->get();
    User::withoutGlobalScope('age')->get(); // for anonymous
    // remove all of the global scopes
    User::withoutGlobalScopes()->get();
    // remove some of the global scopes
    User::withoutGlobalScopes([
      FirstScope::class, SecondScope::class
    ])->get();
  
Query Local Scopes

    // define common sets of constraints that you may easily re-use
    // should always return a query builder instance
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // only include popular users
      public function scopePopular($query) {
        return $query->where('votes', '>', 100);
      }
      // only include active users
      public function scopeActive($query) {
        return $query->where('active', 1);
      }
    }

    // --- call scope methods when querying the model
    // dont include the scope prefix when calling the method
    // chain calls to various scopes
    $users = App\User::popular()->active()->orderBy('created_at')->get();

    // --- or - combining multiple Eloquent model scopes
    $users = App\User::popular()
      ->orWhere(
        function (Builder $query) {
          $query->active();
        })->get();
    // higher order orWhere method
    $users = App\User::popular()
      ->orWhere
      ->active()->get();

    // --- dynamic scopes, scope that accepts parameters
    // scope parameters should be defined after the $query parameter
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // scope a query to only include users of a given type
      public function scopeOfType($query, $type) {
        return $query->where('type', $type);
      }
    }
    // now pass the parameters when calling the scope
    $users = App\User::ofType('admin')->get();
  
Events , Observers

    // execute code each time a specific model class is saved or updated
    // each event receives the instance of the model through its constructor
    // hook into the following points in a model lifecycle:
    //   retrieved, creating, created, updating, updated,
    //   saving, saved, deleting, deleted, restoring, restored
    // saved and updated model events will not be fired for mass update
    // use event listeners to handle the events

    // --- $dispatchesEvents
    // maps various points of the Eloquent model lifecycle to own event classes
    namespace App;
    use App\Events\UserSaved;
    use App\Events\UserDeleted;
    use Illuminate\Notifications\Notifiable;
    use Illuminate\Foundation\Auth\User as Authenticatable;
    class User extends Authenticatable {
      use Notifiable;
      protected $dispatchesEvents = [
        'saved' => UserSaved::class,
        'deleted' => UserDeleted::class,
      ];
    }

    // --- Observers - group all of listeners into a single class
    // php artisan make:observer UserObserver --model=User
    // will place the new observer in App/Observers directory
    namespace App\Observers;
    use App\User;
    class UserObserver {
      // handle the User "created" event
      public function created(User $user) {
        //
      }
      // handle the User "updated" event
      public function updated(User $user) {
        //
      }
      // handle the User "deleted" event
      public function deleted(User $user) {
        //
      }
    }
    // --- observe - register an observe on the model
    // in the boot method of one of service providers
    namespace App\Providers;
    use App\User;
    use App\Observers\UserObserver;
    use Illuminate\Support\ServiceProvider;
    class AppServiceProvider extends ServiceProvider {
      // register any application services
      public function register() {
        // ...
      }

      // bootstrap any application services
      public function boot() {
        User::observe(UserObserver::class);
      }
    }
  
Relationships
--- ONE TO ONE

    // --- hasOne
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // get the phone record associated with the user
      public function phone() {
        return $this->hasOne('App\Phone');
        // Phone model is automatically assumed to have a user_id foreign key
        // override this convention with second argument
        return $this->hasOne('App\Phone', 'foreign_key');
        // use relationship value other than id for User with third argument
        return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
      }
      public function latestPost() {
        return $this->hasOne(\App\Post::class)->latest();
      }
    }
    // ...
      $phone = User::find(1)->phone;
      $users = User::with('latestPost')->get()->sortByDesc('latestPost.created_at');
    // attach a phone to the user, no need to define user_id
      $user = User::find(1);
      $phone = new Phone;
      $phone->address = "Some address in New York";
      $user->phone()->save($phone);
    // delete related relationship entry
      $user = User::find(1);
      $user->phone()->delete();

    // --- belongsTo - inverse of a hasOne
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class Phone extends Model {
      // get the user that owns the phone
      public function user() {
        // Eloquent will try to match
        // the user_id from the Phone model to an id on the User model
        return $this->belongsTo('App\User');
        // override foreign key on the Phone model (when is not user_id)
        return $this->belongsTo('App\User', 'foreign_key');
        // if Phone primary key is not id
        // or you wish to join the child model to a different column
        return $this->belongsTo('App\User', 'foreign_key', 'other_key');
      }
    }
    // ...
      $phone_user = Phone::find(1)->user;
      // update a model attached via a BelongsTo relationship and receive mass assignment update protection and events
      $post->user()->update(['foo' => 'bar']); // ad-hoc query, no mass assignment protection or events
      $post->user->update(['foo' => 'bar']); // model update, provides mass assignment protection and events
  
--- ONE TO MANY

    // single model owns any amount of other models
    // --- hasMany
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class Post extends Model {
      // get the comments for the blog post
      public function comments() {
        // Eloquent will assume the foreign key on the Comment model is post_id
        return $this->hasMany('App\Comment');
        // override the foreign and local keys
        return $this->hasMany('App\Comment', 'foreign_key');
        return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
        return $this->hasMany('App\User')->where('approved', 1)->orderBy('email');
      }
      // separate relationship
      function publishedBooks() {
        return $this->hasMany('App\Book')->where('published', 1);
        // ->orderBy('title');
      }
    }
    // ...
      $comments = App\Post::find(1)->comments;
      foreach ($comments as $comment) {
        // ...
      }
      $comment = App\Post::find(1)->comments()->where('title', 'foo')->first();
    // ...
      $post = App\Post::find(1);
      $post->comments()->create([ 'title' => 'Harry Potter' ]);
      $post->comments()->createMany([
        [ 'title' => 'Harry Potter' ],
        [ 'title' => 'Harry Potter Returns' ]
      ]);

    // --- belongsTo
    // inverse of a hasMany relationship, defined on the child model
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class Comment extends Model {
      // get the post that owns the comment
      public function post() {
        return $this->belongsTo('App\Post');
        return $this->belongsTo('App\Post', 'foreign_key');
        return $this->belongsTo('App\Post', 'foreign_key', 'other_key');
      }
    }
    // ...
      $comment = App\Comment::find(1);
      echo $comment->post->title;
    // ...

    // levels of relationships
    class Author extends Model {
      function books() {
        return $this->hasMany('App\Book');
      }
    }
    class Book extends Model {
      function chapters() {
        return $this->hasMany('App\Chapter');
      }
    }
    // load several relationships in one sentence
    // result will contain all chapters for every book,
    // which you can loop through, without any additional SQL queries
    $author = Author::with('books.chapters')->find(1);
  
--- MANY TO MANY

    // --- belongsToMany
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // the roles that belong to the user
      public function roles() {
        // many users may have the role of "Admin"
        // to define this relationship, three database tables are needed:
        // users, roles, and role_user
        // role_user table is derived from the alphabetical order
        // of the related model names, and contains the user_id and role_id columns
        return $this->belongsToMany('App\Role');
        // override alphabetical order of two related model name join
        return $this->belongsToMany('App\Role',
          'role_user');
        // customize the column names of the keys on the table
        return $this->belongsToMany('App\Role',
          'role_user', 'user_id', 'role_id');
      }
    }
    // ...
      $user = App\User::find(1);
      foreach ($user->roles as $role) {
        // ...
      }
      $roles = App\User::find(1)->roles()->orderBy('name')->get();
    // ...
    // to define the inverse of a many-to-many relationship
    // place another call to belongsToMany on related model
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class Role extends Model {
      // the users that belong to the role
      public function users() {
        // usual table and key customization options are available
        return $this->belongsToMany('App\User');
      }
    }

    // --- pivot - access the intermediate table
    // may be used like any other Eloquent model
    $user = App\User::find(1);
    foreach ($user->roles as $role) {
      echo $role->pivot->created_at;
    }
    // --- specify pivot table extra attributes (if not only model keys)
    return $this->belongsToMany('App\Role')
                ->withPivot('column1', 'column2');
    // --- to automatically maintain created_at and updated_at timestamps
    return $this->belongsToMany('App\Role')->withTimestamps();
    // --- rename intermediate table
    return $this->belongsToMany('App\Podcast')
                ->as('subscription')
                ->withTimestamps();
    // ...
      // access using customized name
      $users = User::with('podcasts')->get();
      foreach ($users->flatMap->podcasts as $podcast) {
        echo $podcast->subscription->created_at;
      }
    // ...
    // --- filter the results returned by belongsToMany
    return $this->belongsToMany('App\Role')
                ->wherePivot('approved', 1);
    return $this->belongsToMany('App\Role')
                ->wherePivotIn('priority', [1, 2]);

    // --- custom many-to-many pivot model, should extend
    // Illuminate\Database\Eloquent\Relations\Pivot
    // custom polymorphic many-to-many pivot models should extend
    // Illuminate\Database\Eloquent\Relations\MorphPivot
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class Role extends Model {
      // users that belong to the role
      public function users() {
        return $this->belongsToMany('App\User')
                    ->using('App\RoleUser');
      }
    }
    // defining the RoleUser model, we will extend the Pivot
    namespace App;
    use Illuminate\Database\Eloquent\Relations\Pivot;
    class RoleUser extends Pivot {
      // ...
      // indicates if the IDs are auto-incrementing
      public $incrementing = true;
    }
    // --- combine using and withPivot to retrieve columns from the intermediate table
    // retrieve the created_by and updated_by columns from the RoleUser
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class Role extends Model {
      // the users that belong to the role
      public function users() {
        return $this->belongsToMany('App\User')
                    ->using('App\RoleUser')
                    ->withPivot([
                        'created_by',
                        'updated_by'
                    ]);
      }
    }
    // --- Pivot models may not use the SoftDeletes trait
    // to do so, convert pivot model to an actual Eloquent model
  
--- hasOneThrough - links models through a single intermediate relation

    // each supplier has one user,
    // each user is associated with one user history record,
    // supplier model may access the user history through the user
    users
      id - integer
      supplier_id - integer
    suppliers
      id - integer
    history
      id - integer
      user_id - integer
    // ---
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class Supplier extends Model {
      // get the user history
      public function userHistory() {
        return $this->hasOneThrough(
          'App\History',  // final model to access
          'App\User'      // intermediate model
          // typical foreign key conventions will be used for relationship queries
          // customize relationship keys:
          'supplier_id',  // foreign key on the intermediate model (users table)
          'user_id',      // foreign key on the final model (history table)
          'id',           // local key (suppliers table)
          'id'            // local key of the intermediate model (users table)
        );
      }
    }
  
--- hasManyThrough - access distant relations via an intermediate relation

    // Country model might have many Post models
    // through an intermediate User model
    countries
      id - integer
      name - string
    users
      id - integer
      country_id - integer
      name - string
    posts
      id - integer
      user_id - integer
      title - string
    // gather all blog posts for a given country via $country->posts
    // ---
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class Country extends Model {
      public function posts() {
        return $this->hasManyThrough(
          'App\Post',   // final model to access
          'App\User',   // intermediate model
          // typical foreign key conventions will be used for relationship queries
          // customize relationship keys:
          'country_id', // foreign key on the intermediate model (users table)
          'user_id',    // foreign key on the final model (posts table)
          'id',         // local key (countries table)
          'id'          // local key of the intermediate model (users table)
        );
      }
    }

    // getting all the comments through posts
    User hasManyThrough Comment, PostController
    // ...
      public function comments() {
        return $this->hasManyThrough(Comment::class, Post::class);
      }
    // ...
    // then
    $user->comments; // collection of all the comments
  
--- morphOne - One To One (Polymorphic)

    // target model can belong to more than one type of model on a single association
    // ---
    // blog Post and a User may share a polymorphic relation to an Image model.
    // single list of unique images that are used for both blog posts and user accounts
    posts
      id - integer
      name - string
    users
      id - integer
      name - string
    images
      id - integer
      url - string
      imageable_id - integer
      imageable_type - string // class name of the parent model
    // ---
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class Image extends Model {
      // get all of the owning imageable models
      public function imageable() {
        // parent from the polymorphic model
        return $this->morphTo();
      }
    }
    class Post extends Model {
      // get the post image
      public function image() {
        return $this->morphOne('App\Image', 'imageable');
      }
    }
    class User extends Model {
      // get the user image
      public function image() {
        return $this->morphOne('App\Image', 'imageable');
      }
    }
    // ...
    // retrieve the image for a post
    $post = App\Post::find(1);
    $image = $post->image;
    $image = App\Image::find(1);
    // parent who asks for image, Post or User instance
    $imageable = $image->imageable;
  
--- morphMany - One To Many (Polymorphic)

    // target model can belong to more than one type of model on a single association
    // ---
    // users can "comment" on both posts and videos
    // use a single comments table for both of these scenarios
    posts
      id - integer
      title - string
      body - text
    videos
      id - integer
      title - string
      url - string
    comments
      id - integer
      body - text
      commentable_id - integer
      commentable_type - string
    // ---
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class Comment extends Model {
      // get all of the owning commentable models
      public function commentable() {
        // owner of a polymorphic relation from the polymorphic model
        return $this->morphTo();
      }
    }
    class Post extends Model {
      // get all of the post comments
      public function comments() {
        return $this->morphMany('App\Comment', 'commentable');
      }
    }
    class Video extends Model {
      // get all of the video comments
      public function comments() {
        return $this->morphMany('App\Comment', 'commentable');
      }
    }
    // ...
    // access all of the comments for a post
    $post = App\Post::find(1);
    foreach ($post->comments as $comment) {
      // ...
    }
    $comment = App\Comment::find(1);
    // return Post or Video instance, depending on which type of model owns the comment
    $commentable = $comment->commentable;
  
--- morphedByMany - Many To Many (Polymorphic)

    // blog Post and Video model could share a polymorphic relation to a Tag model
    // use a single list of unique tags that are shared across blog posts and videos
    // ---
    posts
      id - integer
      name - string
    videos
      id - integer
      name - string
    tags
      id - integer
      name - string
    taggables
      tag_id - integer
      taggable_id - integer
      taggable_type - string
    // ---
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class Post extends Model {
      // get all of the tags for the post
      public function tags() {
        return $this->morphToMany('App\Tag', 'taggable');
      }
    }
    // define a method for each of its related models
    class Tag extends Model {
      // get all of the posts that are assigned this tag
      public function posts() {
        return $this->morphedByMany('App\Post', 'taggable');
      }
      // get all of the videos that are assigned this tag
      public function videos() {
        return $this->morphedByMany('App\Video', 'taggable');
      }
    }
    // ...
    // access all of the tags for a post
    $post = App\Post::find(1);
    foreach ($post->tags as $tag) {
      // ...
    }
    // owner of a polymorphic relation from the polymorphic model
    // posts or videos methods on the Tag model
    $tag = App\Tag::find(1);
    foreach ($tag->videos as $video) {
      // ...
    }

    // custom polymorphic many-to-many pivot models should extend
    // Illuminate\Database\Eloquent\Relations\MorphPivot
  
--- Custom Polymorphic Types

    // define a "morph map" to instruct Eloquent
    // to use a custom name for each model instead of the class name.
    // Comment may belong to a Post or a Video,
    // the default commentable_type would be either App\Post or App\Video.
    // decouple database from application internal structure
    use Illuminate\Database\Eloquent\Relations\Relation;
    Relation::morphMap([
      'posts' => 'App\Post',
      'videos' => 'App\Video',
    ]);
    // register morphMap in the boot function o
    f AppServiceProvider
    // or create a separate service provider

    // custom polymorphic many-to-many pivot models should extend
    // Illuminate\Database\Eloquent\Relations\MorphPivot
  
Querying Relations

    // Eloquent relationships are defined via methods,
    // call those methods to obtain an instance of the relationship
    // without actually executing the relationship queries.
    // all types of Eloquent relationships also serve as query builders
    // use any of the query builder methods
    // continue to chain constraints onto the relationship query
    // before finally executing the SQL against database
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // get all of the posts for the user
      public function posts() {
        return $this->hasMany('App\Post');
      }
    }
    // ...
    $user = App\User::find(1);
    $user->posts()->where('active', 1)->get(); // ->first(), ->count()

    // --- access the relationship as if it were a property
    // do not add additional constraints to an Eloquent relationship query
    $user = App\User::find(1);
    foreach ($user->posts as $post) {
      // ...
    }
    // "lazy loaded", relationship only loads data when you actually access them
    // pre-load relationships they know will be accessed after loading the model
    // significant reduction in SQL queries for loading a model relations

    // --- has , orHas - existence of a relationship
    // posts that have at least one comment
    $posts = App\Post::has('comments')->get();
    // posts that have three or more comment
    $posts = App\Post::has('comments', '>=', 3)->get();
    // "dot" notation - posts that have at least one comment and vote
    $posts = App\Post::has('comments.votes')->get();

    // --- whereHas , orWhereHas
    // add customized constraints to a relationship constraint
    use Illuminate\Database\Eloquent\Builder;
    // posts with at least one comment containing words like foo%
    $posts = App\Post::whereHas('comments', function ($query) {
      $query->where('content', 'like', 'foo%');
    })->get();
    // posts with at least ten comments containing words like foo%
    $posts = App\Post::whereHas('comments', function ($query) {
      $query->where('content', 'like', 'foo%');
    }, '>=', 10)->get();

    // --- whereHasMorph
    use Illuminate\Database\Eloquent\Builder;
    // comments associated to posts or videos with a title like foo%
    $comments = App\Comment::whereHasMorph(
        'commentable',
        ['App\Post', 'App\Video'],
        function (Builder $query) {
            $query->where('title', 'like', 'foo%');
        }
    )->get();
    // comments associated to posts with a title not like foo%
    $comments = App\Comment::whereDoesntHaveMorph(
        'commentable',
        'App\Post',
        function (Builder $query) {
            $query->where('title', 'like', 'foo%');
        }
    )->get();
    // use the $type parameter to add different constraints depending on the related model
    use Illuminate\Database\Eloquent\Builder;
    $comments = App\Comment::whereHasMorph(
        'commentable',
        ['App\Post', 'App\Video'],
        function (Builder $query, $type) {
            $query->where('title', 'like', 'foo%');

            if ($type === 'App\Post') {
                $query->orWhere('content', 'like', 'foo%');
            }
        }
    )->get();
    // provide * instead of passing an array as a wildcard
    // retrieve all the possible polymorphic types from the db
    // Laravel executes an additional query in order to perform this operation
    use Illuminate\Database\Eloquent\Builder;
    $comments = App\Comment::whereHasMorph('commentable', '*', function (Builder $query) {
        $query->where('title', 'like', 'foo%');
    })->get();

    // --- doesntHave , orDoesntHave - absence of a relationship
    // all blog posts that dont have any comments
    $posts = App\Post::doesntHave('comments')->get();

    // --- orWhere clauses will be logically grouped
    // at the same level as the relationship constraint
    $user->posts()
          ->where('active', 1)
          ->orWhere('votes', '>=', 100)
          ->get();
    // select * from posts
    // where user_id = ? and active = 1 or votes >= 100
    // use constraint groups to logically group the conditional checks
    $user->posts()
        ->where(function ($query) {
            return $query->where('active', 1)
                         ->orWhere('votes', '>=', 100);
        })->get();
    // select * from posts
    // where user_id = ? and (active = 1 or votes >= 100)

    // --- whereDoesntHave , orWhereDoesntHave - doesntHave with conditions
    $posts = App\Post::whereDoesntHave('comments', function (Builder $query) {
      // checking the content of a comment
      $query->where('content', 'like', 'foo%');
    })->get();
    //  "dot" notation with nested relationship
    $posts = App\Post::whereDoesntHave('comments.author', function (Builder $query) {
      $query->where('banned', 1);
    })->get();

    // --- withCount - count results from a relationship without actually loading them
    // will place a {relation}_count column on resulting models
    $posts = App\Post::withCount('comments')->get();
    foreach ($posts as $post) {
      echo $post->comments_count;
    }
    // count multiple relations and add constraints to the queries
    $posts = App\Post::withCount(['votes', 'comments' => function ($query) {
      $query->where('content', 'like', 'foo%');
    }])->get();
    echo $posts[0]->votes_count;
    echo $posts[0]->comments_count;
    // alias the relationship count result, allow multiple counts on the same relationship
    $posts = App\Post::withCount([
      'comments',
      'comments as pending_comments_count' => function ($query) {
        $query->where('approved', false);
      }
    ])->get();
    echo $posts[0]->comments_count;
    echo $posts[0]->pending_comments_count;
    // call withCount after the select method !
    $posts = App\Post::select(['title', 'body'])->withCount('comments')->get();
    echo $posts[0]->title;
    echo $posts[0]->body;
    echo $posts[0]->comments_count;

    // --- any level far relation
    // User belongsToMany Tag - User subscribed to many tags
    // Tag belongsToMany Post - Post migh be tagged by many tags
    // User hasMany Post - User is the author of many posts
    // Post hasMany Comment - each Post has many comments
    $user->load([
    // it can be any relation type here and any level of nesting
    'tags.posts'
      // passing $posts by reference, and,
      // if it was never declared, it will be created now
      => function ($q) use (&$posts) {
        // simply execute the query on the posts table
        // and store its result, a collection, in our $posts variable
        $posts = $q->get()
          // duplicates are likely to occur for a m-m relation,
          // remove them with collections unique()
          ->unique();
    }]);
    // also:
    $user = User::with('tags.posts')->find($someId);
    $postsArray = $user->tags->pluck('posts');
    $posts = (new Collection($postsArray))->collapse()->unique();
    // ...
    $user->tags->pluck('posts')->collapse()->unique();
  
Eager Loading

    // avoid multiple queries execution in case:
    // ...
    public function author() {
      return $this->belongsTo('App\Author');
    }
    //...
    $books = App\Book::all();
    foreach ($books as $book) {
      echo $book->author->name; // query for each book to retrieve the author !!!
    }

    // --- with - specify which relationships should be loaded
    $books = App\Book::with('author')->get();
    foreach ($books as $book) {
      echo $book->author->name;
    }
    // only two queries will be executed
    // SELECT * FROM books;
    // SELECT * FROM authors WHERE id IN (1, 2, 3, 4, 5, ...);

    // --- "dot" syntax - eager load nested relationships
    // all of the book authors and all of the author personal contacts
    $books = App\Book::with('author.contacts')->get();

    // --- pecify which columns of the relationship to retrieve
    // always include id column and any relevant foreign key columns in the list !!!
    $books = App\Book::with('author:id,name')->get();

    // --- $with - always load some relationships when retrieving a model
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class Book extends Model {
      // relationships that should always be loaded
      protected $with = ['author'];
      public function author() {
        return $this->belongsTo('App\Author');
      }
    }
    // then, to remove an item from the $with property for a single query
        $books = App\Book::without('author')->get();

    // --- eager load a relationship and specify additional query conditions
    $users = App\User::with(['posts' => function ($query) {
      // only eager load posts where title contains the word first
      $query->where('title', 'like', '%first%');
    }])->get();
    // call other query builder methods to further customize the eager loading operation
    $users = App\User::with(['posts' => function ($query) {
      $query->orderBy('created_at', 'desc');
    }])->get();
    // limit and take query builder methods may not be used when constraining eager loads !!!

    // --- load - lazy eager loading
    // eager load a relationship after the parent model has already been retrieved
    // dynamically decide whether to load related models
    $books = App\Book::all();
    if ($someCondition) {
      $books->load('author', 'publisher');
    }
    // set additional query constraints on the eager loading query
    $books->load(['author' => function ($query) {
      $query->orderBy('published_date', 'asc');
    }]);

    // --- loadMissing - load a relationship only when it has not already been loaded
    public function format(Book $book) {
      $book->loadMissing('author');
      return [
        'name' => $book->name,
        'author' => $book->author->name
      ];
    }

    // --- loadMorph - eager load a morphTo relationship,
    // and nested relationships on the various entities
    // that may be returned by that relationship
    use Illuminate\Database\Eloquent\Model;
    class ActivityFeed extends Model {
      // get the parent of the activity feed record
      public function parentable() {
        return $this->morphTo();
      }
    }
    // Event, Photo, and Post models may create ActivityFeed models
    // Event models belong to a Calendar model,
    // Photo models are associated with Tag models,
    // Post models belong to an Author model
    // retrieve ActivityFeed model instances and eager load all parentable models
    // and their respective nested relationships
    $activities = ActivityFeed::with('parentable')
                  ->get()
                  ->loadMorph('parentable', [
                    Event::class => ['calendar'],
                    Photo::class => ['tags'],
                    Post::class => ['author'],
                  ]);
  
Inserting/Updating Related Models

    // adding new models to relationships
    // use also findOrNew, firstOrNew, firstOrCreate and updateOrCreate

    // insert a new Comment for a Post model
    // insert the Comment directly from the relationship save() method
    // instead of manually setting the post_id attribute on the Comment
    $comment = new App\Comment(['message' => 'A new comment.']);
    $post = App\Post::find(1);
    // obtain an instance of the relationship,
    // method will automatically add the appropriate post_id
    $post->comments()->save($comment);
    // save() method accepts an array of additional intermediate table attribute
    App\User::find(1)->roles()->save($role, ['expires' => $expires]);

    // --- saveMany - save multiple related models
    $post = App\Post::find(1);
    $post->comments()->saveMany([
      new App\Comment(['message' => 'A new comment.']),
      new App\Comment(['message' => 'Another comment.']),
    ]);

    // --- push - save model and all of its associated relationships
    $post = App\Post::find(1);
    $post->comments[0]->message = 'Message';
    $post->comments[0]->author->name = 'Author Name';
    $post->push();

    // --- create
    // accept an array of attributes, creates a model, and inserts it into the database
    // difference between save is that accepts a plain PHP array
    // take in consideration attribute mass assignment rules
    $post = App\Post::find(1);
    $comment = $post->comments()->create([
      'message' => 'A new comment.',
    ]);

    // --- createMany - create multiple related models
    // use also findOrNew, firstOrNew, firstOrCreate, updateOrCreate
    $post = App\Post::find(1);
    $post->comments()->createMany([
      [ 'message' => 'A new comment.' ],
      [ 'message' => 'Another new comment.' ],
    ]);

    // --- associate
    // updating belongsTo relationship, will set the foreign key on the child model
    $account = App\Account::find(10);
    $user->account()->associate($account);
    $user->save();

    // --- dissociate
    // removing belongsTo relationship, will set the relationship foreign key to null
    $user->account()->dissociate();
    $user->save();

    // --- withDefault
    // belongsTo, hasOne, hasOneThrough, and morphOne relationships
    // allow you to define a default model that will be returned
    // if the given relationship is null
    // return an empty App\User model if no user is attached to the post
    public function user() {
      return $this->belongsTo('App\User')->withDefault();
    }
    // populate the default model with attributes
    // get the author of the post
    public function user() {
      return $this->belongsTo('App\User')->withDefault([
        'name' => 'Guest Author',
      ]);
    }
    public function user() {
      return $this->belongsTo('App\User')->withDefault(function ($user) {
        $user->name = 'Guest Author';
      });
    }

    // --- attach
    // inserting a record in the intermediate table that joins the models
    // user can have many roles and a role can have many users
    // attach a role to a user
    $user = App\User::find(1);
    $user->roles()->attach($roleId);
    // pass an array of additional data to be inserted into the intermediate table
    $user->roles()->attach(
      $roleId, [
        'expires' => $expires
      ]
    );
    // arrays of IDs as input
    $user->roles()->attach([
      1 => ['expires' => $expires],
      2 => ['expires' => $expires]
    ]);

    // --- detach - remove a many-to-many relationship record
    // delete the appropriate record out of the intermediate table
    // both models will remain in the database
    // detach a single role from the user
    $user->roles()->detach($roleId);
    // detach all roles from the user
    $user->roles()->detach();
    // arrays of IDs as input
    $user->roles()->detach([1, 2, 3]);

    // --- sync - construct many-to-many associations
    // accepts an array of IDs to place on the intermediate table
    // any IDs that are NOT in the given array will be removed from the intermediate table
    // after operation, only IDs in the given array will exist in the intermediate table
    $user->roles()->sync([1, 2, 3]);
    // pass additional intermediate table values
    $user->roles()->sync([1 => ['expires' => true], 2, 3]);

    // --- syncWithoutDetaching - to not detach existing IDs
    $user->roles()->syncWithoutDetaching([1, 2, 3]);

    // --- toggle - "toggles" the attachment status of the given IDs
    // if the given ID is currently attached, it will be detached
    // if it is currently detached, it will be attached
    $user->roles()->toggle([1, 2, 3]);

    // --- updateExistingPivot - update an existing row in pivot table
    $user = App\User::find(1);
    $user->roles()->updateExistingPivot($roleId, $attributes);
  
touch(), Touching Parent Timestamps

    // --- touch($attribute = null)
    // update the current timestamp, updated_at time of $article
    $article = Article::find($id);
    $article->touch();

    // --- touches
    // update parent timestamp when child model is updated
    // when a Comment model is updated,
    // "touch" the updated_at timestamp of the owning Post
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class Comment extends Model {
      // all of the relationships to be touched
      protected $touches = ['post'];
      // get the post that the comment belongs to
      public function post() {
        return $this->belongsTo('App\Post');
      }
    }
    // ...
    $comment = App\Comment::find(1);
    $comment->text = 'Edit to this comment!';
    $comment->save();
  
Eloquent Collections

    // all multi-result sets returned by Eloquent
    // are instances of the Illuminate\Database\Eloquent\Collection object,
    // including results retrieved via the get method or accessed via a relationship
    // extends the Laravel base collection and inherits its methods

    // all collections also serve as iterators,
    // loop over them as if they were simple PHP arrays
    $users = App\User::where('active', 1)->get();
    foreach ($users as $user) {
      echo $user->name;
    }

    // collections are much more powerful than arrays
    // expose a variety of map / reduce operations that may be chained
    // remove all inactive models and gather the first name for each remaining user:
    $users = App\User::all();
    $names = $users->reject(function ($user) {
      return $user->active === false;
    })
    ->map(function ($user) {
      return $user->name;
    });

    // most Eloquent collection methods return a new instance of an Eloquent collection,
    // pluck, keys, zip, collapse, flatten and flip methods return a base collection instance
    // if a map operation returns a collection that does not contain any Eloquent models,
    // it will be automatically cast to a base collection

    // Illuminate\Database\Eloquent\Collection class provides superset of methods
    // aids with managing model collections
    // most methods return Illuminate\Database\Eloquent\Collection instances
    // some methods return a base Illuminate\Support\Collection instance

    // --- contains($key, $operator = null, $value = null)
    // determine if a given model instance is contained by the collection
    // accepts a primary key or a model instance
    $users->contains(1);
    $users->contains(User::find(1));

    // --- diff($items)
    // returns all of the models that are not present in the given collection
    use App\User;
    $users = $users->diff(User::whereIn('id', [1, 2, 3])->get());

    // --- except($keys)
    // returns all of the models that do not have the given primary keys
    $users = $users->except([1, 2, 3]);

    // --- find($key)
    // finds a model that has a given primary key
    // if $key is a model instance, attempt to return a model matching the primary key
    // if $key is an array of keys, return all models which match the $keys using whereIn()
    $users = User::all();
    $user = $users->find(1);

    // --- fresh($with = [])
    // retrieves a fresh instance of each model in the collection from the database
    // any specified relationships will be eager loaded
    $users = $users->fresh();
    $users = $users->fresh('comments');

    // --- intersect($items)
    // returns all of the models that are also present in the given collection
    use App\User;
    $users = $users->intersect(User::whereIn('id', [1, 2, 3])->get());

    // --- load($relations)
    // eager loads the given relationships for all models in the collection
    $users->load('comments', 'posts');
    $users->load('comments.author');

    // --- loadMissing($relations)
    // eager loads the given relationships for all models in the collection
    // if the relationships are not already loaded
    $users->loadMissing('comments', 'posts');
    $users->loadMissing('comments.author');

    // --- modelKeys
    // returns the primary keys for all models in the collection
    $users->modelKeys();  // [1, 2, 3, 4, 5]

    // --- makeVisible($attributes)
    // makes visible attributes that are typically "hidden" on each model in the collection
    $users = $users->makeVisible(['address', 'phone_number']);

    // --- makeHidden($attributes)
    // hides attributes that are typically "visible" on each model in the collection
    $users = $users->makeHidden(['address', 'phone_number']);

    // --- only($keys)
    // returns all of the models that have the given primary keys
    $users = $users->only([1, 2, 3]);

    // --- unique($key = null, $strict = false)
    // returns all of the unique models in the collection
    // any models of the same type with the same primary key as another model
    // in the collection are removed
    $users = $users->unique();

    // --- CUSTOM COLLECTIONS

    // override the newCollection method on model
    namespace App;
    use App\CustomCollection;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // create a new Eloquent Collection instance
      public function newCollection(array $models = []) {
        return new CustomCollection($models);
      }
    }
    // once you have defined a newCollection method,
    // you will receive an instance of custom collection
    // anytime Eloquent returns a Collection instance of that model
    // if you would like to use a custom collection for every model in application,
    // override the newCollection method on a base model class that is extended by all of models
  
Attribute Accessors & Mutators

    // column value modification on retrieval OR set
    // encrypt/decrypt a value stored in the database
    // cast date fields to Carbon instances or even cast text fields to JSON

    // --- ACCESSOR

    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // get the user first name
      public function getFirstNameAttribute($value) {
        // manipulate and return original value of the column
        // which is passed to the accessor
        return ucfirst($value);
      }
      // also use to return new, computed values from existing attributes
      // get the user full name
      public function getFullNameAttribute() {
        return "{$this->first_name} {$this->last_name}";
      }
    }
    // access the first_name attribute on a model instance
    $user = App\User::find(1);
    $firstName = $user->first_name;
    // append computed values to the array/JSON representations of model

    // --- MUTATOR

    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // set the user first name
      public function setFirstNameAttribute($value) {
        // apply the strtolower function to the name
        // and set its resulting value in the internal $attributes array
        $this->attributes['first_name'] = strtolower($value);
      }
      function getFullNameAttribute() {
        return $this->attributes['first_name'].' '.$this->attributes['last_name'];
      }
    }
    $user = App\User::find(1);
    $user->first_name = 'Sally';
    // order the results after we get them, with sortBy
    $clients = Client::get()->sortBy('full_name');

    // --- Date Mutators

    // by default, Eloquent will convert the created_at and updated_at columns
    // to instances of Carbon, which extends the PHP DateTime class
    // add additional date attributes by setting the $dates property of model
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // attributes that should be mutated to dates
      protected $dates = [
        'seen_at',
      ];
    }
    // disable the default created_at and updated_at timestamps
    // by setting the public $timestamps property of model to false
    // when a column is considered a date, you may set its value to:
    // UNIX timestamp, date string (Y-m-d), date-time string, or a DateTime/Carbon instance
    // date value will be correctly converted and stored in database:
    $user = App\User::find(1);
    $user->deleted_at = now();
    $user->save();
    // when retrieving attributes that are listed in $dates property,
    // they will automatically be cast to Carbon instances,
    // allowing you to use any of Carbon methods on attributes:
    $user = App\User::find(1);
    return $user->deleted_at->getTimestamp();
    // by default, timestamps are formatted as 'Y-m-d H:i:s'
    // to customize the timestamp format, set the $dateFormat property on model
    // determines how date attributes are stored in the database,
    // and their format when the model is serialized to an array or JSON:
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class Flight extends Model {
      // storage format of the model date columns
      protected $dateFormat = 'U';
    }

    // --- Attribute Casting

    // $casts property on model provides method of converting attributes to common data types
    // should be an array where the key is the name of the attribute being cast
    // and the value is the type you wish to cast the column to
    // supported cast types are:
    //   integer, real, float, double, decimal:<digits> (decimal:2),
    //   string, boolean, object, array, collection, date, datetime, and timestamp
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // cast the is_admin attribute, as an integer (0 or 1) to a boolean value
      protected $casts = [
        'is_admin' => 'boolean',
      ];
    }
    // will always be cast to a boolean when accessed,
    // even if the underlying value is stored in the database as an integer:
    $user = App\User::find(1);
    if ($user->is_admin) {
      // ...
    }

    // --- Array & JSON Casting

    // add "array" cast to database JSON or TEXT field type that contains serialized JSON
    // automatically deserialize the attribute to a PHP array
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // attributes that should be cast to native types
      protected $casts = [
        'options' => 'array',
      ];
    }
    // automatically deserialized from JSON into a PHP array on access of options attribute
    // and serialized back into JSON for storage when you set
    $user = App\User::find(1);
    $options = $user->options;
    $options['key'] = 'value';
    $user->options = $options;
    $user->save();

    // --- Date Casting

    // when using the date or datetime cast type, specify the date format,
    // format will be used when the model is serialized to an array or JSON
    // attributes that should be cast to native types
    protected $casts = [
      'created_at' => 'datetime:Y-m-d',
    ];
  
API Resources - transform a model into an array for JSON response

    // --- RESOURCE CLASS

    // represents a single model that needs to be transformed into JSON
    // defines a toArray($request) method which returns the array of attributes to convert
    namespace App\Http\Resources;
    use Illuminate\Http\Resources\Json\JsonResource;
    class User extends JsonResource {
      // transform the resource into an array
      public function toArray($request) {
        return [
          'id' => $this->id,
          'name' => $this->name,
          'email' => $this->email,
          'created_at' => $this->created_at,
          'updated_at' => $this->updated_at,
          // use Post resource collection method
          // to add the user blog posts to the resource response
          'posts' => PostResource::collection($this->posts),
          // when - conditional attributes
          'secret' => $this->when(Auth::user()->isAdmin(), 'secret-value'),
          // calculate the resulting value only if the given condition is true
          'secret' => $this->when(Auth::user()->isAdmin(), function () {
            return 'secret-value';
          }),
          // mergeWhen - merging conditional attributes
          // should not be used within arrays that mix string and numeric keys
          // or within arrays with numeric keys that are not ordered sequentially
          $this->mergeWhen(Auth::user()->isAdmin(), [
            'first-secret' => 'value',
            'second-secret' => 'value',
          ]),
          // whenLoaded - conditionally load a relationship, accepts the name
          // allows controller to decide which relationships should be loaded on the model
          // resource can easily include them only when they have actually been loaded
          // if the relationship has not been loaded,
          // the posts key will be removed from the resource response entirely
          'posts' => PostResource::collection($this->whenLoaded('posts')),
          // whenPivotLoaded - conditionally include data from the intermediate tables
          // of many-to-many relationships
          'expires_at' => $this->whenPivotLoaded('role_user', function () {
            return $this->pivot->expires_at;
          }),
          // whenPivotLoadedAs - if intermediate table is using an accessor
          'expires_at' => $this->whenPivotLoadedAs('subscription', 'role_user', function () {
            return $this->subscription->expires_at;
          }),
        ];
      }
      // Laravel resets keys, they are in simple numerical order
      // return a resource collection from a route and preserve collection keys
      public $preserveKeys = true;
    }
    // return resource from route or controller
    use App\User;
    use App\Http\Resources\User as UserResource;
    Route::get('/user', function () {
      return new UserResource(User::find(1));
    });
    // with preserved collection keys
    Route::get('/user', function () {
      return UserResource::collection(User::all()->keyBy->id);
    });
    // collection() method for creating the resource instance in route or controller
    // resource collection on the fly
    Route::get('/user', function () {
      return UserResource::collection(User::all());
    });

    // --- RESOURCE COLLECTIONS

    // allow any addition of meta data needed to be returned with the collection
    // create a dedicated resource to represent the collection:
    //   php artisan make:resource UserCollection
    // $this->collection is automatically populated
    // with the result of mapping each item of the collection to its singular resource class
    namespace App\Http\Resources;
    use Illuminate\Http\Resources\Json\ResourceCollection;
    // singular resource class is assumed to be the collection class name
    // without the trailing Collection string
    // attempt to map the given user instances into the User resource
    class UserCollection extends ResourceCollection {
      // transform the resource collection into an array
      public function toArray($request) {
        return parent::toArray($request);
        // return additional meta data about a resource
        // any additional links will be merged with the links provided by the paginator
        return [
          'data' => $this->collection,
          'links' => [
            'self' => 'link-value',
          ],
        ];
      }
      // wrapping nested resources
      // transform the resource collection into an array
      public function toArray($request) {
        return ['data' => $this->collection];
      }
      // with - get additional data that should be returned with the resource array
      // only when the resource is the outer-most resource being rendered
      public function with($request) {
        return [
          'meta' => [
            'key' => 'value',
          ],
        ];
      }
      // customize behavior
      // resource that this resource collects
      public $collects = 'App\Http\Resources\Member';
    }
    // resource collection returned from a route or controller
    use App\User;
    use App\Http\Resources\UserCollection;
    Route::get('/users', function () {
      return new UserCollection(User::all());
    });

    // --- ---

    // additional - add top-level data when constructing resource instances
    // in route or controller, available on all resources,
    // accepts an array of data that should be added to the resource response
      return (new UserCollection(User::all()->load('roles')))
        ->additional(['meta' => [
          'key' => 'value',
        ]]);

    // --- CUSTOMIZE HTTP RESPONSE BEFORE IT IS SENT TO THE CLIENT

    // chain the response method onto the resource
    // will return an Illuminate\Http\Response instance,
    // allowing you full control of the response headers
    use App\User;
    use App\Http\Resources\User as UserResource;
    Route::get('/user', function () {
        return (new UserResource(User::find(1)))
                    ->response()
                    ->header('X-Value', 'True');
    });
    // OR, define a withResponse method within the resource itself
    // called when the resource is returned as the outer-most resource in a response
    namespace App\Http\Resources;
    use Illuminate\Http\Resources\Json\JsonResource;
    class User extends JsonResource {
      // transform the resource into an array
      public function toArray($request) {
        return [
          'id' => $this->id,
        ];
      }
      // customize the outgoing response for the resource
      public function withResponse($request, $response) {
        $response->header('X-Value', 'True');
      }
    }

    // pass a paginator instance to the collection method of a resource
    // or to a custom resource collection
    use App\User;
    use App\Http\Resources\UserCollection;
    Route::get('/users', function () {
      return new UserCollection(User::paginate());
    });

    // --- DISABLE WRAPPING OF THE OUTER-MOST RESOURCE

    // avoid such form: {"data":[{"id":1,"name":...
    // use the withoutWrapping method on the base resource class
    // call from AppServiceProvider or another service provider loaded on every request
    namespace App\Providers;
    use Illuminate\Support\ServiceProvider;
    use Illuminate\Http\Resources\Json\JsonResource;
    class AppServiceProvider extends ServiceProvider {
      // register bindings in the container
      public function register() {
        // ...
      }
      // bootstrap any application services
      public function boot() {
        JsonResource::withoutWrapping();
      }
    }

    // ---

    {
      "data": [
        {
          "id": 1, ... }
      ],
      "links":{
        "first": "http://example.com/pagination?page=1",
        "last": "http://example.com/pagination?page=1",
        "prev": null,
        "next": null
      },
      "meta":{
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "path": "http://example.com/pagination",
        "per_page": 15,
        "to": 10,
        "total": 10
      }
    }
  
Serialization - convert models and relationships to arrays or JSON

    // --- toArray - convert a model and its loaded relationships to an array
    // is recursive, cast any Illuminate\Contracts\Support\Arrayable attributes to an array
    $user = App\User::with('roles')->first();
    return $user->toArray();
    // convert entire collections of models to arrays
    $users = App\User::all();
    return $users->toArray();

    // --- attributesToArray - convert only a model attributes to an array
    $user = App\User::first();
    return $user->attributesToArray();

    // --- toJson - convert a model to JSON
    $user = App\User::find(1);
    return $user->toJson();
    return $user->toJson(JSON_PRETTY_PRINT);
    // cast a model or collection to a string,
    // will automatically call the toJson method on the model or collection
    $user = App\User::find(1);
    return (string) $user;
    // return Eloquent objects directly from routes or controllers
    // since models and collections are converted to JSON when cast to a string
    Route::get('users', function () {
      return App\User::all();
    });
    // relationship JSON attribute will be "snake_case"

    // --- $hidden - hiding attributes from json
    // when hiding relationships, use relationship method name
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // attributes that should be hidden for arrays
      protected $hidden = ['password'];
    }

    // --- $visible
    // white-list attributes that should be included in array and JSON representation
    // all other attributes will be hidden
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // attributes that should be visible in arrays
      protected $visible = ['first_name', 'last_name'];
    }

    // --- makeVisible , makeHidden
    // temporarily modifying attribute visibility
    return $user->makeVisible('attribute')->toArray();
    return $user->makeHidden('attribute')->toArray();

    // --- $appends - add attributes that do not have a corresponding column in database
    // first define an accessor
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // get the administrator flag for the user
      public function getIsAdminAttribute() {
        return $this->attributes['admin'] == 'yes';
      }
    }
    // then add the attribute name to the appends property on the model
    // attribute names are in "snake case", even accessor is in "camel case"
    namespace App;
    use Illuminate\Database\Eloquent\Model;
    class User extends Model {
      // accessors to append to the model array form
      // attribute will be included in both the model array and JSON representations
      // will respect the $visible and $hidden settings
      protected $appends = ['is_admin'];
    }

    // --- append , setAppends
    // instruct a single model instance to append attributes
    // or, override the entire array of appended properties
    return $user->append('is_admin')->toArray();
    return $user->setAppends(['is_admin'])->toArray();

    // --- $casts - customizing date format per attribute
    protected $casts = [
      'birthday' => 'date:Y-m-d',
      'joined_at' => 'datetime:Y-m-d H:00',
    ];

    // --- Carbon::serializeUsing
    // returns string representation of the date for JSON
    namespace App\Providers;
    use Illuminate\Support\Carbon;
    use Illuminate\Support\ServiceProvider;
    class AppServiceProvider extends ServiceProvider {
      // register bindings in the container
      public function register() {
        // ...
      }
      // bootstrap any application services
      public function boot() {
        Carbon::serializeUsing(function ($carbon) {
          return $carbon->format('U');
        });
      }
    }
  

Collections


    // --- collect - creating a collection
    $collection = collect([1, 2, 3]);

    // --- macro - extending collections
    // add additional methods to the Collection class at run time
    // add a toUpper method to the Collection class:
    use Illuminate\Support\Str;
    Collection::macro('toUpper', function () {
      return $this->map(function ($value) {
        return Str::upper($value);
      });
    });
    $collection = collect(['first', 'second']);
    $upper = $collection->toUpper();
    // ['FIRST', 'SECOND']

    // --- chain collection methods
    // create a new collection instance from the array
    // run the strtoupper function on each element
    // then remove all empty elements
    $collection = collect(['taylor', 'abigail', null])
      ->map(function ($name) {
        return strtoupper($name);
      })
      ->reject(function ($name) {
        return empty($name);
      });
  
Methods
all - returns the underlying array represented by the collection

      collect([1, 2, 3])->all(); // [1, 2, 3]
    
avg , max , min , sum , median

      $average = collect([
        ['foo' => 10],
        ['foo' => 10],
        ['foo' => 20],
        ['foo' => 40]
      ])->avg('foo'); // 20
      $average = collect([1, 1, 2, 4])->avg(); // 2

      // --- max - maximum value

      $max = collect([['foo' => 10], ['foo' => 20]])->max('foo'); // 20
      $max = collect([1, 2, 3, 4, 5])->max(); // 5

      // --- min - minimum value

      $min = collect([['foo' => 10], ['foo' => 20]])->min('foo'); // 10
      $min = collect([1, 2, 3, 4, 5])->min(); // 1

      // --- sum - sum of all items

      collect([1, 2, 3, 4, 5])->sum(); // 15
      // if the collection contains nested arrays or objects,
      // pass a key to use for determining which values to sum
      $collection = collect([
        ['name' => 'JavaScript: The Good Parts', 'pages' => 176],
        ['name' => 'JavaScript: The Definitive Guide', 'pages' => 1096],
      ]);
      $collection->sum('pages'); // 1272
      // pass own callback to determine which values of the collection to sum
      $collection = collect([
        ['name' => 'Chair', 'colors' => ['Black']],
        ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']],
        ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']],
      ]);
      $collection->sum(function ($product) {
        return count($product['colors']);
      }); // 6

      // --- median - median value

      $median = collect([
        ['foo' => 10],
        ['foo' => 10],
        ['foo' => 20],
        ['foo' => 40]
      ])->median('foo'); // 15
      $median = collect([1, 1, 2, 4])->median(); // 1.5
    
chunk - breaks collection into smaller collections of a given size

      $collection = collect([1, 2, 3, 4, 5, 6, 7]);
      $chunks = $collection->chunk(4);
      $chunks->toArray();
      // [[1, 2, 3, 4], [5, 6, 7]]
      // useful in views when working with a grid system such as Bootstrap
      @foreach ($products->chunk(3) as $chunk)
      <div class="row">
        @foreach ($chunk as $product)
          <div class="col-xs-4">{{ $product->name }}</div>
        @endforeach
      </div>
      @endforeach
    
collapse - collapses a collection of arrays into a single, flat

      $collection = collect([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
      $collapsed = $collection->collapse();
      $collapsed->all();
      // [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
combine - combines the values

      // combines as keys, with the values of another array or collection
      $collection = collect(['name', 'age']);
      $combined = $collection->combine(['George', 29]);
      $combined->all();
      // ['name' => 'George', 'age' => 29]
    
concat - appends array or collection values onto the end

      $collection = collect(['John Doe']);
      $concatenated = $collection->concat(['Jane Doe'])->concat(['name' => 'Johnny Doe']);
      $concatenated->all();
      // ['John Doe', 'Jane Doe', 'Johnny Doe']
    
contains , some , containsStrict - whether the collection contains a given item

      $collection = collect(['name' => 'Desk', 'price' => 100]);
      $collection->contains('Desk'); // true
      $collection->contains('New York'); // false
      // pass a key/value to determine if it exists in the collection
      $collection = collect([
        ['product' => 'Desk', 'price' => 200],
        ['product' => 'Chair', 'price' => 100],
      ]);
      $collection->contains('product', 'Bookcase');  // false
      // pass a callback to the contains method to perform own truth test
      $collection = collect([1, 2, 3, 4, 5]);
      $collection->contains(function ($value, $key) {
          return $value > 5;
      }); // false

      // some - alias for the contains method

      // contains method uses "loose" comparisons when checking item values,
      // string with an integer value will be considered equal to an integer of the same value
      // use the containsStrict method to filter using "strict" comparisons
    
count , countBy

      $collection = collect([1, 2, 3, 4]);
      $collection->count();  // 4

      // --- countBy - counts the occurrences of values
      // by default, counts occurrences of every element
      $collection = collect([1, 2, 2, 2, 3]);
      $counted = $collection->countBy();
      $counted->all(); // [1 => 1, 2 => 3, 3 => 1]

      // pass a callback to count all items by a custom value
      $collection = collect([
        'alice@gmail.com',
        'bob@yahoo.com',
        'carlos@gmail.com'
      ]);
      $counted = $collection->countBy(function ($email) {
        return substr(strrchr($email, "@"), 1);
      });
      $counted->all(); // ['gmail.com' => 2, 'yahoo.com' => 1]
    
crossJoin - Cartesian product with all possible permutations

      $collection = collect([1, 2]);
      $matrix = $collection->crossJoin(['a', 'b']);
      $matrix->all();
      /*
        [
          [1, 'a'],
          [1, 'b'],
          [2, 'a'],
          [2, 'b'],
        ]
      */
      $collection = collect([1, 2]);
      $matrix = $collection->crossJoin(['a', 'b'], ['I', 'II']);
      $matrix->all();
      /*
        [
          [1, 'a', 'I'],
          [1, 'a', 'II'],
          [1, 'b', 'I'],
          [1, 'b', 'II'],
          [2, 'a', 'I'],
          [2, 'a', 'II'],
          [2, 'b', 'I'],
          [2, 'b', 'II'],
        ]
      */
    
dd - dump items and ends execution of the script

      $collection = collect(['John Doe', 'Jane Doe']);
      $collection->dd();
      /*
        Collection {
          #items: array:2 [
            0 => "John Doe"
            1 => "Jane Doe"
          ]
        }
      */
      // stop executing the script, use the dump method
    
dump - dump items

      $collection = collect(['John Doe', 'Jane Doe']);
      $collection->dump();
      /*
        Collection {
          #items: array:2 [
            0 => "John Doe"
            1 => "Jane Doe"
          ]
        }
      */
      // to stop executing the script after dumping the collection, use the dd method
    
diff , diffAssoc , diffKeys

      // compares collection against collection or plain PHP array
      // returns values in the original collection
      // that are not present in collection
      $collection = collect([1, 2, 3, 4, 5]);
      $diff = $collection->diff([2, 4, 6, 8]);
      $diff->all(); // [1, 3, 5]

      // --- diffAssoc

      // return the key/value pairs in the original collection
      // that are not present in the given collection
      $collection = collect([
        'color' => 'orange',
        'type' => 'fruit',
        'remain' => 6
      ]);
      $diff = $collection->diffAssoc([
        'color' => 'yellow',
        'type' => 'fruit',
        'remain' => 3,
        'used' => 6
      ]);
      $diff->all(); // ['color' => 'orange', 'remain' => 6]

      // --- diffKeys

      // compares based on keys
      // will return the key/value pairs in the original collection
      // that are not present in the given collection
      $collection = collect([
        'one' => 10,
        'two' => 20,
        'three' => 30,
        'four' => 40,
        'five' => 50,
      ]);
      $diff = $collection->diffKeys([
        'two' => 2,
        'four' => 4,
        'six' => 6,
        'eight' => 8,
      ]);
      $diff->all(); // ['one' => 10, 'three' => 30, 'five' => 50]
    
duplicates - retrieves and returns duplicate values

      $collection = collect(['a', 'b', 'a', 'c', 'b']);
      $collection->duplicates(); // [ 2 => 'a', 4 => 'b' ]
    
each , eachSpread - iterates over the items(nested) and pass each item to a callback

      $collection->each(function ($item, $key) {
        // ...
      });
      // stop iterating through the items, return false from callback
      $collection->each(function ($item, $key) {
        if ( /* some condition */ ) {
          return false;
        }
      });

      // --- eachSpread - iterate and pass each nested item value into the callback

      $collection = collect([['John Doe', 35], ['Jane Doe', 33]]);
      $collection->eachSpread(function ($name, $age) {
        // ...
      });
      // stop iterating through the items by returning false from the callback
      $collection->eachSpread(function ($name, $age) {
        return false;
      });
    
every - verify that all elements pass a given truth test

      collect([1, 2, 3, 4])->every(function ($value, $key) {
        return $value > 2;
      }); // false
      // if the collection is empty, every will return true
      $collection = collect([]);
      $collection->every(function($value, $key) {
        return $value > 2;
      }); // true
    
except , only - exclude/get items with the specified keys

      $collection = collect([
        'product_id' => 1,
        'price' => 100,
        'discount' => false
      ]);
      $filtered = $collection->except(['price', 'discount']);
      $filtered->all(); // ['product_id' => 1]

      $collection = collect([
        'product_id' => 1,
        'name' => 'Desk',
        'price' => 100,
        'discount' => false
      ]);
      $filtered = $collection->only(['product_id', 'name']);
      $filtered->all(); // ['product_id' => 1, 'name' => 'Desk']
    
filter , reject - filter using callback

      $collection = collect([1, 2, 3, 4]);
      $filtered = $collection->filter(function ($value, $key) {
        return $value > 2;
      });
      $filtered->all(); // [3, 4]
      // if no callback is supplied,
      // all entries that are equivalent to false will be removed
      $collection = collect([1, 2, 3, null, false, '', 0, []]);
      $collection->filter()->all(); // [1, 2, 3]

      // inverse callback should return true if the item should be removed
      $collection = collect([1, 2, 3, 4]);
      $filtered = $collection->reject(function ($value, $key) {
        return $value > 2;
      });
      $filtered->all(); // [1, 2]
    
first , firstWhere - element that passes test OR with key/value pair

      collect([1, 2, 3, 4])->first(function ($value, $key) {
        return $value > 2;
      }); // 3
      // call with no arguments to get the first element in the collection
      // if the collection is empty, null is returned
      collect([1, 2, 3, 4])->first(); // 1

      // --- firstWhere - first element with key/value pair

      $collection = collect([
        ['name' => 'Regena', 'age' => null],
        ['name' => 'Linda', 'age' => 14],
        ['name' => 'Diego', 'age' => 23],
        ['name' => 'Linda', 'age' => 84],
      ]);
      $collection->firstWhere('name', 'Linda'); // ['name' => 'Linda', 'age' => 14]
      // call with an operator
      $collection->firstWhere('age', '>=', 18); // ['name' => 'Diego', 'age' => 23]
      // pass one argument to the firstWhere method
      $collection->firstWhere('age'); // ['name' => 'Linda', 'age' => 14]
    
flatten - multi-dimensional collection into a single dimension

      $collection = collect(['name' => 'taylor', 'languages' => ['php', 'javascript']]);
      $flattened = $collection->flatten();
      $flattened->all(); // ['taylor', 'php', 'javascript'];
      // pass the function a "depth" argument:
      $collection = collect([
        'Apple' => [ ['name' => 'iPhone 6S', 'brand' => 'Apple'] ],
        'Samsung' => [ ['name' => 'Galaxy S7', 'brand' => 'Samsung'] ],
      ]);
      $products = $collection->flatten(1);
      $products->values()->all();
      /*
        [
          ['name' => 'iPhone 6S', 'brand' => 'Apple'],
          ['name' => 'Galaxy S7', 'brand' => 'Samsung'],
        ]
      */
    
flip - swap keys with their corresponding values

      $collection = collect(['name' => 'taylor', 'framework' => 'laravel']);
      $flipped = $collection->flip();
      $flipped->all(); // ['taylor' => 'name', 'laravel' => 'framework']
    
forget - remove an item by its key

      $collection = collect(['name' => 'taylor', 'framework' => 'laravel']);
      $collection->forget('name');
      $collection->all(); // ['framework' => 'laravel']
      // unlike most other methods, does not return a new modified collection
      // modifies the collection it is called on
    
forPage - return items that would be present on a given page number

      $collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9]);
      $chunk = $collection->forPage(2, 3); // page number and the number of items
      $chunk->all(); // [4, 5, 6]

      // forPageBeforeId(int $perPage = 15, int|null $lastId = 0, string $column = 'id')
    
get - item at a given key

      // if the key does not exist, null is returned
      $collection = collect(['name' => 'taylor', 'framework' => 'laravel']);
      $value = $collection->get('name'); // taylor
      // pass a default value as the second argument
      $collection = collect(['name' => 'taylor', 'framework' => 'laravel']);
      $value = $collection->get('foo', 'default-value'); // default-value
      // pass a callback
      // result will be returned if the specified key does not exist
      $collection->get('email', function () {
        return 'default-value';
      }); // default-value
    
groupBy - group items by a given key

      $collection = collect([
        ['account_id' => 'account-x10', 'product' => 'Chair'],
        ['account_id' => 'account-x10', 'product' => 'Bookcase'],
        ['account_id' => 'account-x11', 'product' => 'Desk'],
      ]);
      $grouped = $collection->groupBy('account_id');
      $grouped->toArray();
      /*
        [
          'account-x10' => [
            ['account_id' => 'account-x10', 'product' => 'Chair'],
            ['account_id' => 'account-x10', 'product' => 'Bookcase'],
          ],
          'account-x11' => [
            ['account_id' => 'account-x11', 'product' => 'Desk'],
          ],
        ]
      */
      // pass a callback
      // should return the value you wish to key the group by
      $grouped = $collection->groupBy(function ($item, $key) {
        return substr($item['account_id'], -3);
      });
      $grouped->toArray();
      /*
        [
          'x10' => [
            ['account_id' => 'account-x10', 'product' => 'Chair'],
            ['account_id' => 'account-x10', 'product' => 'Bookcase'],
          ],
          'x11' => [
            ['account_id' => 'account-x11', 'product' => 'Desk'],
          ],
        ]
      */
      // multiple grouping criteria passed as an array
      // each array element will be applied to the corresponding level
      // within a multi-dimensional array
      $data = new Collection([
        10 => ['user' => 1, 'skill' => 1, 'roles' => ['Role_1', 'Role_3']],
        20 => ['user' => 2, 'skill' => 1, 'roles' => ['Role_1', 'Role_2']],
        30 => ['user' => 3, 'skill' => 2, 'roles' => ['Role_1']],
        40 => ['user' => 4, 'skill' => 2, 'roles' => ['Role_2']],
      ]);
      $result = $data->groupBy([
        'skill',
        function ($item) {
          return $item['roles'];
        },
      ], $preserveKeys = true);
      /*
        [
          1 => [
            'Role_1' => [
              10 => ['user' => 1, 'skill' => 1, 'roles' => ['Role_1', 'Role_3']],
              20 => ['user' => 2, 'skill' => 1, 'roles' => ['Role_1', 'Role_2']],
            ],
            'Role_2' => [
              20 => ['user' => 2, 'skill' => 1, 'roles' => ['Role_1', 'Role_2']],
            ],
            'Role_3' => [
              10 => ['user' => 1, 'skill' => 1, 'roles' => ['Role_1', 'Role_3']],
            ],
          ],
          2 => [
            'Role_1' => [
              30 => ['user' => 3, 'skill' => 2, 'roles' => ['Role_1']],
            ],
            'Role_2' => [
              40 => ['user' => 4, 'skill' => 2, 'roles' => ['Role_2']],
            ],
          ],
        ];
      */
    
has - determines if a given key exists

      $collection = collect(['account_id' => 1, 'product' => 'Desk', 'amount' => 5]);
      $collection->has('product');  // true
      $collection->has(['product', 'amount']); // true
      $collection->has(['amount', 'price']); // false
    
implode - join items

      // arguments depend on the type of items in the collection
      // if the collection contains arrays or objects,
      // pass the key of the attributes you wish to join,
      // and the "glue" string you wish to place between the values
      $collection = collect([
        ['account_id' => 1, 'product' => 'Desk'],
        ['account_id' => 2, 'product' => 'Chair'],
      ]);
      $collection->implode('product', ', '); // Desk, Chair

      // if the collection contains simple strings or numeric values,
      // pass the "glue" as the only argument to the method:
      collect([1, 2, 3, 4, 5])->implode('-'); // '1-2-3-4-5'
    
intersect , intersectByKeys - removes any values/keys that are not present

      // resulting collection will preserve the original collection keys
      $collection = collect(['Desk', 'Sofa', 'Chair']);
      $intersect = $collection->intersect(['Desk', 'Chair', 'Bookcase']);
      $intersect->all(); // [0 => 'Desk', 2 => 'Chair']

      // --- intersectByKeys

      $collection = collect([
        'serial' => 'UX301', 'type' => 'screen', 'year' => 2009
      ]);
      $intersect = $collection->intersectByKeys([
        'reference' => 'UX404', 'type' => 'tab', 'year' => 2011
      ]);
      $intersect->all(); // ['type' => 'screen', 'year' => 2009]
    
isEmpty , isNotEmpty - true, if collection is(not) empty; otherwise, false

      collect([])->isEmpty(); // true
      collect([])->isNotEmpty(); // false
    
join - joins values with a string

      collect(['a', 'b', 'c'])->join(', '); // 'a, b, c'
      collect(['a', 'b', 'c'])->join(', ', ', and '); // 'a, b, and c'
      collect(['a', 'b'])->join(', ', ' and '); // 'a and b'
      collect(['a'])->join(', ', ' and '); // 'a'
      collect([])->join(', ', ' and '); // ''
    
keyBy - keys collection by key

      // if multiple items have the same key,
      // only the last one will appear in the new collection

      $collection = collect([
        ['product_id' => 'prod-100', 'name' => 'Desk'],
        ['product_id' => 'prod-200', 'name' => 'Chair'],
      ]);
      $keyed = $collection->keyBy('product_id');
      $keyed->all();
      /*
        [
          'prod-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],
          'prod-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],
        ]
      */
      // pass a callback to the method
      // should return the value to key the collection by
      $keyed = $collection->keyBy(function ($item) {
        return strtoupper($item['product_id']);
      });
      $keyed->all();
      /*
        [
          'PROD-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],
          'PROD-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],
        ]
      */
    
keys - all keys

      $collection = collect([
        'prod-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],
        'prod-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],
      ]);
      $keys = $collection->keys();
      $keys->all(); // ['prod-100', 'prod-200']
    
last - last element that passes a given truth test

      collect([1, 2, 3, 4])->last(function ($value, $key) {
        return $value < 3;
      }); // 2
      // call with no arguments to get the last element in the collection
      // if the collection is empty, null is returned:
      collect([1, 2, 3, 4])->last(); // 4
    
map , flatMap , mapInto , mapSpread , mapToGroups , mapWithKeys

      // callback is free to modify the item and return it,
      // forming a new collection of modified items
      $collection = collect([1, 2, 3, 4, 5]);
      $multiplied = $collection->map(function ($item, $key) {
        return $item * 2;
      });
      $multiplied->all(); // [2, 4, 6, 8, 10]

      // like most other collection methods,returns a new collection instance
      // does not modify the collection it is called on
      // use transform method to transform the original collection

      // flatMap - array is flattened by a level
      $collection = collect([
        ['name' => 'Sally'],
        ['school' => 'Arkansas'],
        ['age' => 28]
      ]);
      $flattened = $collection->flatMap(function ($values) {
        return array_map('strtoupper', $values);
      });
      $flattened->all();
      // ['name' => 'SALLY', 'school' => 'ARKANSAS', 'age' => '28'];

      // mapInto - iterate and create new instance of class
      // pass the value into the constructor
      class Currency {
        // create a new currency instance
        function __construct(string $code) {
          $this->code = $code;
        }
      }
      $collection = collect(['USD', 'EUR', 'GBP']);
      $currencies = $collection->mapInto(Currency::class);
      $currencies->all();
      // [Currency('USD'), Currency('EUR'), Currency('GBP')]

      // mapSpread - iterates items passing value into the callback
      // callback is free to modify the item and return it,
      // forming a new collection of modified items
      $collection = collect([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
      $chunks = $collection->chunk(2);
      $sequence = $chunks->mapSpread(function ($even, $odd) {
        return $even + $odd;
      });
      $sequence->all(); // [1, 5, 9, 13, 17]

      // mapToGroups - groups items by callback
      // callback should return an associative array containing a single key/value pair,
      // forming a new collection of grouped values
      $collection = collect([
        [ 'name' => 'John Doe', 'department' => 'Sales' ],
        [ 'name' => 'Jane Doe', 'department' => 'Sales' ],
        [ 'name' => 'Johnny Doe', 'department' => 'Marketing' ]
      ]);
      $grouped = $collection->mapToGroups(function ($item, $key) {
        return [$item['department'] => $item['name']];
      });
      $grouped->toArray();
      /*
        [
          'Sales' => ['John Doe', 'Jane Doe'],
          'Marketing' => ['Johnny Doe'],
        ]
      */
      $grouped->get('Sales')->all(); // ['John Doe', 'Jane Doe']

      // mapWithKeys
      // callback should return an associative array containing a single key/value pair
      $collection = collect([
        [
          'name' => 'John',
          'department' => 'Sales',
          'email' => 'john@example.com'
        ],
        [
          'name' => 'Jane',
          'department' => 'Marketing',
          'email' => 'jane@example.com'
        ]
      ]);
      $keyed = $collection->mapWithKeys(function ($item) {
      return [$item['email'] => $item['name']];
      });
      $keyed->all();
      /*
        [
          'john@example.com' => 'John',
          'jane@example.com' => 'Jane',
        ]
      */
    
merge - merges with the original collection

      // if a string key in the given items matches a string key in the original collection,
      // the given items value will overwrite the value in the original collection
      $collection = collect(['product_id' => 1, 'price' => 100]);
      $merged = $collection->merge(['price' => 200, 'discount' => false]);
      $merged->all(); // ['product_id' => 1, 'price' => 200, 'discount' => false]
      // if the given items keys are numeric,
      // the values will be appended to the end
      $collection = collect(['Desk', 'Chair']);
      $merged = $collection->merge(['Bookcase', 'Door']);
      $merged->all(); // ['Desk', 'Chair', 'Bookcase', 'Door']
    
mode - mode value

      $mode = collect([
        ['foo' => 10],
        ['foo' => 10],
        ['foo' => 20],
        ['foo' => 40]
      ])->mode('foo'); // [10]
      $mode = collect([1, 1, 2, 4])->mode(); // [1]
    
nth - every n-th element

      $collection = collect(['a', 'b', 'c', 'd', 'e', 'f']);
      $collection->nth(4); // ['a', 'e']
      // pass an offset as the second argument
      $collection->nth(4, 1); // ['b', 'f']
    
pad - fill array with the value until specified size

      // behaves like the array_pad PHP function
      // to pad to the left, specify a negative size
      // no padding will take place if the absolute value of the given size
      // is less than or equal to the length of the array
      $collection = collect(['A', 'B', 'C']);
      $filtered = $collection->pad(5, 0);
      $filtered->all(); // ['A', 'B', 'C', 0, 0]
      $filtered = $collection->pad(-5, 0);
      $filtered->all(); // [0, 0, 'A', 'B', 'C']
    
partition - separate elements that pass a given truth test

      // may be combined with the list PHP function
      $collection = collect([1, 2, 3, 4, 5, 6]);
      list($underThree, $equalOrAboveThree) =
        $collection->partition(function ($i) {
          return $i < 3;
        });
      $underThree->all(); // [1, 2]
      $equalOrAboveThree->all(); // [3, 4, 5, 6]
    
pipe - pass collection to callback and return the result

      $collection = collect([1, 2, 3]);
      $piped = $collection->pipe(function ($collection) {
        return $collection->sum();
      }); // 6
    
pluck - retrieves all of the values for a given key

      $collection = collect([
        ['product_id' => 'prod-100', 'name' => 'Desk'],
        ['product_id' => 'prod-200', 'name' => 'Chair'],
      ]);
      $plucked = $collection->pluck('name');
      $plucked->all(); // ['Desk', 'Chair']
      // specify how you wish the resulting collection to be keyed
      $plucked = $collection->pluck('name', 'product_id');
      $plucked->all(); // ['prod-100' => 'Desk', 'prod-200' => 'Chair']
      // if duplicate keys exist,
      // last matching element will be inserted into the plucked collection
      $collection = collect([
        ['brand' => 'Tesla',  'color' => 'red'],
        ['brand' => 'Pagani', 'color' => 'white'],
        ['brand' => 'Tesla',  'color' => 'black'],
        ['brand' => 'Pagani', 'color' => 'orange'],
      ]);
      $plucked = $collection->pluck('color', 'brand');
      $plucked->all(); // ['Tesla' => 'black', 'Pagani' => 'orange']
    
prepend - adds an item to the beginning

      $collection = collect([1, 2, 3, 4, 5]);
      $collection->prepend(0);
      $collection->all(); // [0, 1, 2, 3, 4, 5]
      // set the key of the prepended item
      $collection = collect(['one' => 1, 'two' => 2]);
      $collection->prepend(0, 'zero');
      $collection->all(); // ['zero' => 0, 'one' => 1, 'two' => 2]
    
pull , push , shift , pop

      // --- pull - removes and returns an item by its key

      $collection = collect(['product_id' => 'prod-100', 'name' => 'Desk']);
      $collection->pull('name'); // 'Desk'
      $collection->all(); // ['product_id' => 'prod-100']

      // --- push - appends an item to the end

      $collection = collect([1, 2, 3, 4]);
      $collection->push(5);
      $collection->all(); // [1, 2, 3, 4, 5]

      // --- shift - removes and returns first item

      $collection = collect([1, 2, 3, 4, 5]);
      $collection->shift(); // 1
      $collection->all(); // [2, 3, 4, 5]

      // --- pop - removes and returns last item
      $collection = collect([1, 2, 3, 4, 5]);
      $collection->pop(); // 5
      $collection->all(); // [1, 2, 3, 4]
    
put - sets the given key and value

      $collection = collect(['product_id' => 1, 'name' => 'Desk']);
      $collection->put('price', 100);
      $collection->all(); // ['product_id' => 1, 'name' => 'Desk', 'price' => 100]
    
random - returns a random item

      $collection = collect([1, 2, 3, 4, 5]);
      $collection->random(); // 4 - (retrieved randomly)
      // pass an integer to specify how many items you would like to randomly retrieve
      // collection of items is always returned when explicitly passing the number
      $random = $collection->random(3);
      $random->all();// [2, 4, 5] - (retrieved randomly)
      // if the Collection has fewer items than requested
      // method will throw an InvalidArgumentException
    
, reduceSpread - reduce collection

      // --- reduce - reduce collection to a single value
      // will pass the result of each iteration into the subsequent iteration
      $collection = collect([1, 2, 3]);
      $total = $collection->reduce(function ($carry, $item) {
        return $carry + $item;
      }); // 6
      // value for $carry on the first iteration is null;
      // specify its initial value by passing a second argument
      $collection->reduce(function ($carry, $item) {
        return $carry + $item;
      }, 4); // 10
      // --- reduceSpread - reduce collection to an array of values,
      // passing the results of each iteration into the subsequent iteration
      // is similar to the reduce method but can accept multiple initial values
      [$creditsRemaining, $batch] = Image::where('status', 'unprocessed')
      ->get()
      ->reduceSpread(function ($creditsRemaining, $batch, $image) {
          if ($creditsRemaining >= $image->creditsRequired()) {
              $batch->push($image);
              $creditsRemaining -= $image->creditsRequired();
          }
          return [$creditsRemaining, $batch];
      }, $creditsAvailable, collect());
    
reverse - reverses the order of items

      // preserves original keys
      $collection = collect(['a', 'b', 'c', 'd', 'e']);
      $reversed = $collection->reverse();
      $reversed->all();
      /*
        [
          4 => 'e',
          3 => 'd',
          2 => 'c',
          1 => 'b',
          0 => 'a',
        ]
      */
    
search - searches for value and returns its key, otherwise false

      $collection = collect([2, 4, 6, 8]);
      $collection->search(4); // 1
      // search is done using a "loose" comparison,
      // string with an integer value will be considered equal to an integer of the same value
      // use "strict" comparison, pass true as the second argument
      $collection->search('4', true); // false
      // alternatively, pass own callback to search for the first item
      // that passes truth test
      $collection->search(function ($item, $key) {
        return $item > 5;
      }); // 2
    
shuffle - randomly shuffle items

      $collection = collect([1, 2, 3, 4, 5]);
      $shuffled = $collection->shuffle();
      $shuffled->all(); // [3, 2, 5, 1, 4] - (generated randomly)
    
slice - slice starting at the given index

      $collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
      $slice = $collection->slice(4);
      $slice->all(); // [5, 6, 7, 8, 9, 10]
      // limit size of the returned slice
      $slice = $collection->slice(4, 2);
      $slice->all(); // [5, 6]
      // returned slice will preserve keys by default
      // use the values method to avoid this
    
sort , sortBy , sortByDesc , sortKeys , sortKeysDesc

      // sorted collection keeps the original array keys
      // use the values method to reset the keys to consecutively numbered indexes

      // --- sort

      $collection = collect([5, 3, 1, 2, 4]);
      $sorted = $collection->sort();
      $sorted->values()->all(); // [1, 2, 3, 4, 5]
      // pass a callback for advanced sorting
      // calls PHP uasort

      // --- sortBy

      $collection = collect([
        ['name' => 'Desk', 'price' => 200],
        ['name' => 'Chair', 'price' => 100],
        ['name' => 'Bookcase', 'price' => 150],
      ]);
      $sorted = $collection->sortBy('price');
      $sorted->values()->all();
      /*
        [
          ['name' => 'Chair', 'price' => 100],
          ['name' => 'Bookcase', 'price' => 150],
          ['name' => 'Desk', 'price' => 200],
        ]
      */
      // pass own callback to determine how to sort the collection values
      $collection = collect([
        ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']],
        ['name' => 'Chair', 'colors' => ['Black']],
        ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']],
      ]);
      $sorted = $collection->sortBy(function ($product, $key) {
        return count($product['colors']);
      });
      $sorted->values()->all();
      /*
        [
          ['name' => 'Chair', 'colors' => ['Black']],
          ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']],
          ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']],
        ]
      */

      // --- sortByDesc
      // has the same signature as the sortBy, will sort in the opposite order

      // --- sortKeys - sorts by the keys of the underlying associative array

      $collection = collect([
        'id' => 22345,
        'first' => 'John',
        'last' => 'Doe',
      ]);
      $sorted = $collection->sortKeys();
      $sorted->all();
      /*
        [
          'first' => 'John',
          'id' => 22345,
          'last' => 'Doe',
        ]
      */

      // --- sortKeysDesc - sort in the opposite order
    
sole - exactly one element element that passes a given truth test

      // only if it exactly one element matches
      collect([1, 2, 3, 4])->sole(function ($value, $key) {
        return $value === 2;
      }); // 2
      // pass a key/value pair to the sole method,
      // will return the first element in the collection that matches the given pair
      $collection = collect([
        ['product' => 'Desk', 'price' => 200],
        ['product' => 'Chair', 'price' => 100],
      ]);
      $collection->sole('product', 'Chair'); // ['product' => 'Chair', 'price' => 100]
      // call with no argument to get the first element in the collection if there is only one element
      // if there are no elements in the collection that should be returned by the sole method,
      // an \Illuminate\Collections\ItemNotFoundException exception will be thrown
      // if there is more than one element that should be returned,
      // an \Illuminate\Collections\MultipleItemsFoundException will be thrown
      $collection = collect([
        ['product' => 'Desk', 'price' => 200],
      ]);
      $collection->sole(); // ['product' => 'Desk', 'price' => 200]
    
splice - removes and returns a slice of items starting at index

      $collection = collect([1, 2, 3, 4, 5]);
      $chunk = $collection->splice(2);
      $chunk->all(); // [3, 4, 5]
      $collection->all(); // [1, 2]
      // pass a second argument to limit the size of the resulting chunk
      $collection = collect([1, 2, 3, 4, 5]);
      $chunk = $collection->splice(2, 1);
      $chunk->all(); // [3]
      $collection->all(); // [1, 2, 4, 5]
      // pass a third argument containing the new items
      // to replace the items removed from the collection:
      $collection = collect([1, 2, 3, 4, 5]);
      $chunk = $collection->splice(2, 1, [10, 11]);
      $chunk->all(); // [3]
      $collection->all(); // [1, 2, 10, 11, 4, 5]
    
split - breaks a collection into the given number of groups

      $collection = collect([1, 2, 3, 4, 5]);
      $groups = $collection->split(3);
      $groups->toArray(); // [[1, 2], [3, 4], [5]]
    
take - returns new collection with specified number of items

      $collection = collect([0, 1, 2, 3, 4, 5]);
      $chunk = $collection->take(3);
      $chunk->all(); // [0, 1, 2]
      // pass a negative integer
      // take the specified amount of items from the end of the collection
      $collection = collect([0, 1, 2, 3, 4, 5]);
      $chunk = $collection->take(-2);
      $chunk->all(); // [4, 5]
    
tap - do something with the items while not affecting collection itself

      collect([2, 4, 3, 1, 5])
      ->sort()
      ->tap(function ($collection) {
        Log::debug('Values after sorting', $collection->values()->toArray());
      })
      ->shift(); // 1

      // without tap
      $tappable = TappableClass::make();
      $tappable->doSomething();
      $tappable->doSomethingElse();
      $result = $tappable->getResult();
      // with tap method
      $result = tap(TappableClass::make(), function ($tappable) {
          $tappable->doSomething();
          $tappable->doSomethingElse();
      })->getResult();
      // with Tappable trait
      $result = TappableClass::make()->tap(function ($tappable) {
          $tappable->doSomething();
          $tappable->doSomethingElse();
      })->getResult();
    
times - invoke callback a given amount of times and create new collection

      $collection = Collection::times(10, function ($number) {
        return $number * 9;
      });
      $collection->all(); // [9, 18, 27, 36, 45, 54, 63, 72, 81, 90]
      // useful when combined with factories to create Eloquent models
      $categories = Collection::times(3, function ($number) {
        return factory(Category::class)->create(['name' => "Category No. $number"]);
      });
      $categories->all();
      /*
        [
          ['id' => 1, 'name' => 'Category #1'],
          ['id' => 2, 'name' => 'Category #2'],
          ['id' => 3, 'name' => 'Category #3'],
        ]
      */
    
toArray - converts collection into a plain PHP array

      // if the collection values are Eloquent models,
      // models will also be converted to arrays
      $collection = collect(['name' => 'Desk', 'price' => 200]);
      $collection->toArray();
      /*
        [
          ['name' => 'Desk', 'price' => 200],
        ]
      */
      // also converts all of the collection nested objects
      // that are an instance of Arrayable to an array
      // if you want to get the raw underlying array, use the all method
    
toJson - converts collection into a JSON serialized string

      $collection = collect(['name' => 'Desk', 'price' => 200]);
      $collection->toJson(); // '{"name":"Desk", "price":200}'
    
transform - iterates and calls callback to replace items

      // modifies the collection itself
      // create a new collection with map method
      $collection = collect([1, 2, 3, 4, 5]);
      $collection->transform(function ($item, $key) {
        return $item * 2;
      });
      $collection->all(); // [2, 4, 6, 8, 10]
    
union - adds the given array to the collection

      // if the given array contains keys that are already in the original collection,
      // the original collection values will be preferred
      $collection = collect([1 => ['a'], 2 => ['b']]);
      $union = $collection->union([3 => ['c'], 1 => ['b']]);
      $union->all(); // [1 => ['a'], 2 => ['b'], 3 => ['c']]
    
unique , uniqueStrict - returns all of the unique items

      // keeps the original array keys
      // use the values method to reset the keys to consecutively numbered indexes
      $collection = collect([1, 1, 2, 2, 3, 4, 2]);
      $unique = $collection->unique();
      $unique->values()->all(); // [1, 2, 3, 4]
      // specify the key used to determine uniqueness
      // when dealing with nested arrays or objects
      $collection = collect([
        ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'],
        ['name' => 'iPhone 5', 'brand' => 'Apple', 'type' => 'phone'],
        ['name' => 'Apple Watch', 'brand' => 'Apple', 'type' => 'watch'],
        ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'],
        ['name' => 'Galaxy Gear', 'brand' => 'Samsung', 'type' => 'watch'],
      ]);
      $unique = $collection->unique('brand');
      $unique->values()->all();
      /*
        [
          ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'],
          ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'],
        ]
      */
      // pass own callback to determine item uniqueness
      $unique = $collection->unique(function ($item) {
        return $item['brand'].$item['type'];
      });
      $unique->values()->all();
      /*
        [
          ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'],
          ['name' => 'Apple Watch', 'brand' => 'Apple', 'type' => 'watch'],
          ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'],
          ['name' => 'Galaxy Gear', 'brand' => 'Samsung', 'type' => 'watch'],
        ]
      */
      // uses "loose" comparisons when checking item values,
      // string with an integer value will be considered equal to an integer of the same value
      // se the uniqueStrict method to filter using "strict" comparisons
    
when , unless , whenNotEmpty , whenEmpty , unlessEmpty , unlessNotEmpty

      // --- when - execute callback when the first argument evaluates to true

      $collection = collect([1, 2, 3]);
      $collection->when(true, function ($collection) {
        return $collection->push(4);
      });
      $collection->when(false, function ($collection) {
        return $collection->push(5);
      });
      $collection->all(); // [1, 2, 3, 4]
      $collection->when(function ($collection) {
        return false; // This closure is executed...
      }, function ($collection) {
        // Not executed since first closure returned "false"...
        $collection->merge([1, 2, 3]);
      });

      // --- unless - execute callback unless first argument evaluates to true

      $collection = collect([1, 2, 3]);
      $collection->unless(true, function ($collection) {
        return $collection->push(4);
      });
      $collection->unless(false, function ($collection) {
        return $collection->push(5);
      });
      $collection->all(); // [1, 2, 3, 5]

      // --- whenEmpty - execute the given callback when the collection is empty

      $collection = collect(['michael', 'tom']);
      $collection->whenEmpty(function ($collection) {
        return $collection->push('adam');
      });
      $collection->all(); // ['michael', 'tom']
      $collection = collect();
      $collection->whenEmpty(function ($collection) {
        return $collection->push('adam');
      });
      $collection->all(); // ['adam']
      $collection = collect(['michael', 'tom']);
      $collection->whenEmpty(function($collection) {
        return $collection->push('adam');
      }, function($collection) {
        return $collection->push('taylor');
      });
      $collection->all(); // ['michael', 'tom', 'taylor']

      // --- whenNotEmpty - execute callback when the collection is not empty

      $collection = collect(['michael', 'tom']);
      $collection->whenNotEmpty(function ($collection) {
        return $collection->push('adam');
      });
      $collection->all(); // ['michael', 'tom', 'adam']
      $collection = collect();
      $collection->whenNotEmpty(function ($collection) {
        return $collection->push('adam');
      });
      $collection->all(); // []
      $collection = collect();
      $collection->whenNotEmpty(function($collection) {
        return $collection->push('adam');
      }, function($collection) {
        return $collection->push('taylor');
      });
      $collection->all(); // ['taylor']
    
values - keys reset to consecutive integers

      $collection = collect([
        10 => ['product' => 'Desk', 'price' => 200],
        11 => ['product' => 'Desk', 'price' => 200]
      ]);
      $values = $collection->values();
      $values->all();
      /*
        [
          0 => ['product' => 'Desk', 'price' => 200],
          1 => ['product' => 'Desk', 'price' => 200],
        ]
      */
    
where , whereStrict - filters by a given key/value pair

      $collection = collect([
        ['product' => 'Desk', 'price' => 200],
        ['product' => 'Chair', 'price' => 100],
        ['product' => 'Bookcase', 'price' => 150],
        ['product' => 'Door', 'price' => 100],
      ]);
      $filtered = $collection->where('price', 100);
      $filtered->all();
      /*
        [
          ['product' => 'Chair', 'price' => 100],
          ['product' => 'Door', 'price' => 100],
        ]
      */
      // uses "loose" comparisons when checking item values,
      // string with an integer value will be considered equal to an integer of the same value
      // use the whereStrict method to filter using "strict" comparisons
    
where[Not]Between , where[Not]In , whereInstanceOf

      // --- whereBetween - filters within a given range

      $collection = collect([
        ['product' => 'Desk', 'price' => 200],
        ['product' => 'Chair', 'price' => 80],
        ['product' => 'Bookcase', 'price' => 150],
        ['product' => 'Pencil', 'price' => 30],
        ['product' => 'Door', 'price' => 100],
      ]);
      $filtered = $collection->whereBetween('price', [100, 200]);
      $filtered->all();
      /*
        [
          ['product' => 'Desk', 'price' => 200],
          ['product' => 'Bookcase', 'price' => 150],
          ['product' => 'Door', 'price' => 100],
        ]
      */

      // --- whereNotBetween - filters within a given range

      $collection = collect([
        ['product' => 'Desk', 'price' => 200],
        ['product' => 'Chair', 'price' => 80],
        ['product' => 'Bookcase', 'price' => 150],
        ['product' => 'Pencil', 'price' => 30],
        ['product' => 'Door', 'price' => 100],
      ]);
      $filtered = $collection->whereNotBetween('price', [100, 200]);
      $filtered->all();
      /*
        [
          ['product' => 'Chair', 'price' => 80],
          ['product' => 'Pencil', 'price' => 30],
        ]
      */

      // --- whereIn - filters by a given key/value contained within the given array

      $collection = collect([
        ['product' => 'Desk', 'price' => 200],
        ['product' => 'Chair', 'price' => 100],
        ['product' => 'Bookcase', 'price' => 150],
        ['product' => 'Door', 'price' => 100],
      ]);
      $filtered = $collection->whereIn('price', [150, 200]);
      $filtered->all();
      /*
        [
          ['product' => 'Bookcase', 'price' => 150],
          ['product' => 'Desk', 'price' => 200],
        ]
      */
      // uses "loose" comparisons when checking item values,
      // string with an integer value will be considered equal to an integer of the same value
      // se the whereInStrict method to filter using "strict" comparisons

      // --- whereNotIn - filters by a given key/value not contained within the given array

      $collection = collect([
        ['product' => 'Desk', 'price' => 200],
        ['product' => 'Chair', 'price' => 100],
        ['product' => 'Bookcase', 'price' => 150],
        ['product' => 'Door', 'price' => 100],
      ]);
      $filtered = $collection->whereNotIn('price', [150, 200]);
      $filtered->all();
      /*
        [
          ['product' => 'Chair', 'price' => 100],
          ['product' => 'Door', 'price' => 100],
        ]
      */
      // uses "loose" comparisons when checking item values,
      // string with an integer value will be considered equal to an integer of the same value
      // use the whereNotInStrict method to filter using "strict" comparisons

      // --- whereInstanceOf - filters by a given class type

      $collection = collect([
        new User,
        new User,
        new Post,
      ]);
      return $collection->whereInstanceOf(User::class);
    
wrap , unwrap - wraps value into collection , returns underlying items

      // --- wrap - wraps the given value in a collection when applicable

      $collection = Collection::wrap('John Doe');
      $collection->all(); // ['John Doe']
      $collection = Collection::wrap(['John Doe']);
      $collection->all(); // ['John Doe']
      $collection = Collection::wrap(collect('John Doe'));
      $collection->all(); // ['John Doe']

      // --- unwrap - returns underlying items from the given value when applicable

      Collection::unwrap(collect('John Doe')); // ['John Doe']
      Collection::unwrap(['John Doe']); // ['John Doe']
      Collection::unwrap('John Doe'); // 'John Doe'
    
zip - merges at the corresponding index

      $collection = collect(['Chair', 'Desk']);
      $zipped = $collection->zip([100, 200]);
      $zipped->all(); // [['Chair', 100], ['Desk', 200]]
    
Lazy Collections

    // uses PHP generators for large datasets with low memory usage
    // keep only small part of the file in memory at a given time
    use App\Models\LogEntry;
    use Illuminate\Support\LazyCollection;

    // --- CREATING, pass a PHP generator function to the collection 'make' method
    // - processing large file:
    LazyCollection::make(function () {
      $handle = fopen('log.txt', 'r');
      while (($line = fgets($handle)) !== false) {
        yield $line;
      }
    })->chunk(4)->map(function ($lines) {
      return LogEntry::fromLines($lines);
    })->each(function (LogEntry $logEntry) {
      // Process the log entry...
    });
    // - iterate through a lot of Eloquent models:
    $users = User::all()->filter(function ($user) {
        return $user->id > 500;
    });
    // - query builder 'cursor' method returns a LazyCollection instance
    // runs a single query against the database, only keeps one Eloquent model loaded in memory
    // filter callback is not executed until we iterate over each user individually
    $users = User::cursor()->filter(function ($user) {
      return $user->id > 500;
    });
    foreach ($users as $user) {
      echo $user->id;
    }

    // --- METHODS
    // almost all methods available on the Collection class are available.
    // both classes implement the Illuminate\Support\Enumerable, additional:

    // --- takeUntilTimeout()
    // returns a new lazy collection that will enumerate values until the specified time
    // after that time, the collection will then stop enumerating
    $lazyCollection = LazyCollection::times(INF)
      ->takeUntilTimeout(now()->addMinute());
    $lazyCollection->each(function ($number) {
      dump($number);
      sleep(1);
    }); // 1  2 ... 58 59

    // example of application that submits invoices from the database using a cursor
    // schedule task that runs every 15 minutes
    // only processes invoices for a maximum of 14 minutes:
    use App\Models\Invoice;
    use Illuminate\Support\Carbon;
      // ...
      Invoice::pending()->cursor()
        ->takeUntilTimeout(
          Carbon::createFromTimestamp(LARAVEL_START)->add(14, 'minutes')
        )
        ->each(fn ($invoice) => $invoice->submit());

    // --- tapEach()
    // while each method calls callback for each item in the collection right away
    // this, only calls callback as the items are being pulled out of the list one by one
    // nothing has been dumped so far...:
    $lazyCollection = LazyCollection::times(INF)->tapEach(function ($value) {
        dump($value);
    });
    // Three items are dumped...
    $array = $lazyCollection->take(3)->all(); // 1 2 3

    // --- remember() - returns a new lazy collection
    // that will remember any values that have already been enumerated
    // and will not retrieve them again on subsequent collection enumerations
    // no query has been executed yet...:
    $users = User::cursor()->remember();
    // query is executed...
    // first 5 users are hydrated from the database...
    $users->take(5)->all();
    // first 5 users come from the collections cache...
    // rest are hydrated from the database...
    $users->take(20)->all();
  
Higher Order Message

    // each higher order message can be accessed as a dynamic property
    // use to call a method on each object within a collection:
    $users = User::where('votes', '>', 500)->get();
    $users->each->markAsVip();
    // use the sum higher order message
    // to gather the total number of "votes" for a collection of users:
    $users = User::where('group', 'Development')->get();
    return $users->sum->votes;
  

Encryption


    // --- Crypt::encrypt
    // encrypt a secret and store it on an Eloquent model
    namespace App\Http\Controllers;
    use App\User;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Crypt;
    class UserController extends Controller {
      // secret message for the user
      public function storeSecret(Request $request, $id) {
        $user = User::findOrFail($id);
        $user->fill([
          'secret' => Crypt::encrypt($request->secret)
        ])->save();
      }
    }

    // --- Crypt::decrypt
    use Illuminate\Contracts\Encryption\DecryptException;
    // ...
      try {
        $decrypted = Crypt::decrypt($encryptedValue);
      } catch (DecryptException $e) {
        // ...
      }
    // ...

    // --- Crypt::encryptString , Crypt::decryptString
    $encrypted = Crypt::encryptString('Hello world.');
    $decrypted = Crypt::decryptString($encrypted);
  

Error Handling


    // --- report
    // helper, report an exception but continue handling the current request
    public function isValid($value) {
      try {
        // validate the value...
      } catch (Exception $e) {
        report($e);
        return false;
      }
    }

    // --- HTTP exceptions
    abort(404);
    abort(403, 'Unauthorized action.');

    // --- custom HTTP error pages
    // customize the error page for 404 HTTP status codes,
    // create a resources/views/errors/404.blade.php
    // views within this directory should be named to match the HTTP status code
    // HttpException instance will be passed to the view as an $exception variable:
    <h2>{{ $exception->getMessage() }}</h2>
    // publish error page templates, customize them to liking:
    php artisan vendor:publish --tag=laravel-errors

    // ---
    namespace App\Exceptions;
    use Exception;
    use Illuminate\Validation\ValidationException;
    use Illuminate\Auth\Access\AuthorizationException;
    use Illuminate\Database\Eloquent\ModelNotFoundException;
    use Laravel\Lumen\Exceptions\Handler as ExceptionHandler;
    use Symfony\Component\HttpKernel\Exception\HttpException;
    class Handler extends ExceptionHandler {

      // list of the exception types that should not be reported
      protected $dontReport = [
        // \Illuminate\Auth\AuthenticationException::class,
        AuthorizationException::class,
        HttpException::class,
        ModelNotFoundException::class,
        ValidationException::class,
      ];
      // report or log an exception
      public function report(Throwable $exception) {

        // great spot to send exceptions to Sentry, Bugsnag, etc.
        if ($exception instanceof CustomException) {
          // ...
        }

        parent::report($exception);
      }
      // render an exception into an HTTP response
      public function render($request, Throwable $exception) {

        // check the exception type or return own custom response
        if ($exception instanceof CustomException) {
          return response()->view('errors.custom', [], 500);
        }

        return parent::render($request, $exception);
      }
      // overriding the context method
      // information will be included in every exception log message
      protected function context() {
        return array_merge(parent::context(), [
          'foo' => 'bar',
        ]);
      }

    }

    // --- custom exception
    namespace App\Exceptions;
    use Exception;
    class RenderException extends Exception {
      public function report() {
        // ...
      }
      // type-hint any required dependencies of the report method
      // they will automatically be injected into the method by service container
      public function render($request) {
        return response(...);
      }
    }
  

Logging


    use Illuminate\Support\Facades\Log;
    // ...
    Log::emergency($message);
    Log::alert($message);
    Log::critical($message);
    Log::error($message);
    Log::warning($message);
    Log::notice($message);
    Log::info('Showing user profile for user: '.$id);
    Log::debug($message);
    // with contextual data
    Log::info('User failed to login.', ['id' => $user->id]);
    // log a message to a channel other than default
    Log::channel('slack')->info('Something happened!');
    // create an on-demand logging stack consisting of multiple channels
    Log::stack(['single', 'slack'])->info('Something happened!');

    // ---
    namespace App\Http\Controllers;
    use App\User;
    use Illuminate\Support\Facades\Log;
    use App\Http\Controllers\Controller;
    class UserController extends Controller {
      // show profile for user
      public function showProfile($id) {
        Log::info('Showing user profile for user: '.$id);
        return view('user.profile', ['user' => User::findOrFail($id)]);
      }
    }

    // --- config/logging.php
    use Monolog\Handler\StreamHandler;
    use Monolog\Handler\SyslogUdpHandler;
    return [
      // --- default log channel, one of the channels defined in the "channels"
      'default' => env('LOG_CHANNEL', 'stack'),
      // --- configure the log channels
      'channels' => [
        // wrapper to facilitate creating "multi-channel" channels
        'stack' => [
          'driver' => 'stack',
          // aggregates other channels, will have the opportunity to log the message
          'channels' => ['daily'],
          // 'name' => 'channel-name',
        ],

        // single file or path based logger channel (StreamHandler)
        'single' => [
          'driver' => 'single',
          'path' => storage_path('logs/lumen.log'),
          'level' => 'debug',
          // optional:
            // if messages should bubble up to other channels after being handled, default is true
            bubble => true,
            // log file permissions, default is 0644
            permission => 0644,
            // attempt to lock the log file before writing to it, default is false
            locking => false,
        ],
        // RotatingFileHandler based Monolog driver which rotates daily
        'daily' => [
          'driver' => 'daily',
          'path' => storage_path('logs/lumen.log'),
          'level' => 'debug',
          'days' => 14,
          // optional:
            // if messages should bubble up to other channels after being handled, default is true
            bubble => true,
            // log file permissions, default is 0644
            permission => 0644,
            // attempt to lock the log file before writing to it, default is false
            locking => false,
        ],

        // SlackWebhookHandler based Monolog driver
        'slack' => [
          'driver' => 'slack',
          // URL should match incoming webhook that is configured for Slack team
          'url' => env('LOG_SLACK_WEBHOOK_URL'),
          'username' => 'Lumen Log',
          'emoji' => ':boom:',
          'level' => 'critical',
        ],
        // SyslogUdpHandler based Monolog driver
        'papertrail' => [
          'driver' => 'monolog',
          'level' => 'debug',
          'handler' => SyslogUdpHandler::class,
          'handler_with' => [
            'host' => env('PAPERTRAIL_URL'),
            'port' => env('PAPERTRAIL_PORT'),
          ],
        ],
        'stderr' => [
          'driver' => 'monolog',
          'handler' => StreamHandler::class,
          'with' => [
            'stream' => 'php://stderr',
          ],
        ],
        // SyslogHandler based Monolog driver
        'syslog' => [
          'driver' => 'syslog',
          'level' => 'debug',
        ],
        // ErrorLogHandler based Monolog driver
        'errorlog' => [
          'driver' => 'errorlog',
          'level' => 'debug',
        ],

        // Monolog factory driver that may use any supported Monolog handler
        'monolog' => [
          // ...
        ],
        // driver that calls a specified factory to create a channel
        'custom' => [
          // ...
        ],

      ],
    ];
  

Events


    // --- app/Providers/EventServiceProvider.php
    use Illuminate\Support\Facades\Event;

    class EventServiceProvider extends ServiceProvider {
      // manual registration of Closure based events
      public function boot() {
        parent::boot();
        Event::listen('event.name', function ($foo, $bar) {}
        Event::listen('event.*', function ($eventName, array $data) {
          // ...
        });
        Event::listen(function (PodcastProcessed $event) {
          //
        });
        // marked as queueable using the Illuminate\Events\queueable function
        // use function Illuminate\Events\queueable;
        Event::listen(queueable(function (PodcastProcessed $event) {
          // ...
        }));
        // use the onConnection, onQueue, and delay methods to customize the execution of the queued listener
        Event::listen(queueable(function (PodcastProcessed $event) {
          // ...
        })->onConnection('redis')->onQueue('podcasts')->delay(now()->addSeconds(10)));
        // handle anonymous queued listener failures,
        // provide a closure to the catch method while defining the queueable listener:
        // use Illuminate\Support\Facades\Event;
        // use Throwable;
        Event::listen(queueable(function (PodcastProcessed $event) {
          // ...
        })->catch(function (PodcastProcessed $event, Throwable $e) {
          // queued listener failed...
        }));
      }

      // determine if events and listeners should be automatically discovered
      public function shouldDiscoverEvents() {
        return true;
      }
      // all listeners within Listeners directory will be scanned
      // add listener directories that should be used to discover events
      protected function discoverEventsWithin() {
        return [
          $this->app->path('Listeners'),
        ];
      }
      // event listener mappings for the application
      protected $listen = [
        'App\Events\OrderShipped' => [
          'App\Listeners\SendShipmentNotification',
        ],
      ];
    }

    // --- EVENT
    // data container which holds the information related to the event
    // assume generated OrderShipped event receives an Eloquent ORM object
    // container for the Order instance that was purchased:
    namespace App\Events;
    use App\Order;
    use Illuminate\Queue\SerializesModels;
    class OrderShipped {
      // serialize any Eloquent models
      // if the event object is serialized using PHP serialize function
      use SerializesModels;
      public $order;
      // create a new event instance
      public function __construct(Order $order) {
        $this->order = $order;
      }
    }

    // --- LISTENER
    // receives event instance in handle method and perform any actions necessary to respond to the event
    // event:generate - automatically import proper event class and type-hint event on the handle method
    // type-hint any dependencies you need on constructors,
    // they will be resolved via the Laravel service container,
    // so dependencies will be injected automatically
    namespace App\Listeners;
    use App\Events\OrderShipped;
    class SendShipmentNotification {
      // create the event listener
      public function __construct() {
        // ...
      }
      // handle the event
      public function handle(OrderShipped $event) {
        // access the order using $event->order...
        // if required, stop propagation of an event to other listeners
        return false;
      }
    }

    // --- QUEUED EVENT LISTENERS
    // when listener is going to perform a slow task: sending an e-mail or making an HTTP request
    // configure queue and start a queue listener on server or local development environment
    // when this listener is called for an event,
    // it will be automatically queued by the event dispatcher using Laravel queue system
    // if no exceptions are thrown when the listener is executed by the queue,
    // queued job will automatically be deleted after it has finished processing
    namespace App\Listeners;
    use App\Events\OrderShipped;
    use Illuminate\Contracts\Queue\ShouldQueue;
    // to manually access the listener underlying queue job delete and release methods
    use Illuminate\Queue\InteractsWithQueue;
    class SendShipmentNotification implements ShouldQueue {
      // optional, manually handle the event
      use InteractsWithQueue;
      public function handle(OrderShipped $event) {
        if (true) { $this->release(30); }
      }
      // optional, handle a job failure
      // if queued listener exceeds the maximum number of attempts as defined by queue worker
      public function failed(OrderShipped $event, $exception) {
        // ...
      }
      // optional, name of the connection the job should be sent to
      public $connection = 'sqs';
      // optional, name of the queue the job should be sent to
      public $queue = 'listeners';
      // optional, time (seconds) before the job should be processed
      public $delay = 60;
      // ...
    }

    // --- DISPATCHING EVENTS
    // pass an instance of the event to the event helper
    // will dispatch the event to all of its registered listeners
    // call it from anywhere in application
    namespace App\Http\Controllers;
    use App\Order;
    use App\Events\OrderShipped;
    use App\Http\Controllers\Controller;
    class OrderController extends Controller {
      // ship the given order
      public function ship($orderId) {
        $order = Order::findOrFail($orderId);
        // order shipment logic...
        event(new OrderShipped($order));
        // Event::dispatch(new ExampleEvent);
      }
    }

    // --- EVENT SUBSCRIBERS
    // classes that may subscribe to multiple events from within the class itself,
    // allowing to define several event handlers within a single class
    // should define a subscribe method, which will be passed an event dispatcher instance
    // call the listen method on the given dispatcher to register event listeners
    namespace App\Listeners;
    class UserEventSubscriber {
      // handle user login events
      public function handleUserLogin($event) {}
      // handle user logout events
      public function handleUserLogout($event) {}
      // register the listeners for the subscriber
      public function subscribe($events) {
        $events->listen(
          'Illuminate\Auth\Events\Login',
          'App\Listeners\UserEventSubscriber@handleUserLogin'
        );
        $events->listen(
          'Illuminate\Auth\Events\Logout',
          'App\Listeners\UserEventSubscriber@handleUserLogout'
        );
      }
    }
    // register event subscribers with the event dispatcher
    // using the $subscribe property on the EventServiceProvider
    namespace App\Providers;
    use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
    class EventServiceProvider extends ServiceProvider {
      // event listener mappings for the application
      protected $listen = [
        //
      ];
      // subscriber classes to register
      protected $subscribe = [
        'App\Listeners\UserEventSubscriber',
      ];
    }

    // --- AUTOMATIC EVENT DISCOVERY
    // available for Laravel 5.8.9 or later !
    // any listener class method that begins with handle,
    // register those methods as event listeners for the event
    // that is type-hinted in the method signature
    use App\Events\PodcastProcessed;
    class SendPodcastProcessedNotification {
      // handle the given event
      public function handle(PodcastProcessed $event) {
        // ...
      }
    }
  

Mail


    // register the mailer and its aliases within bootstrap/app.php
    $app->configure('mail');
    $app->alias('mailer', Illuminate\Mail\Mailer::class);
    $app->alias('mailer', Illuminate\Contracts\Mail\Mailer::class);
    $app->alias('mailer', Illuminate\Contracts\Mail\MailQueue::class);

    // following configuration options should also be available to .env file
    MAIL_MAILER=smtp
    MAIL_HOST=smtp.mailtrap.io
    MAIL_PORT=2525
    MAIL_USERNAME=
    MAIL_PASSWORD=
    MAIL_ENCRYPTION=tls
    MAIL_FROM_ADDRESS=hello@example.com
    MAIL_FROM_NAME="Example app"
  
API drivers

    // --- Mailgun
    composer require symfony/mailgun-mailer symfony/http-client
    // next, install Guzzle
    // then set the driver option in config/mail.php configuration file to "mailgun"
    // next, verify that config/services.php configuration file contains
    'mailgun' => [
      'domain' => env('MAILGUN_DOMAIN'),
      'secret' => env('MAILGUN_SECRET'),
      // define region endpoint if not "US":
      'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'),
    ],

    // --- Postmark
    composer require symfony/postmark-mailer symfony/http-client
    // next, install Guzzle
    // then set the driver option in config/mail.php configuration file to "postmark"
    // verify that config/services.php configuration file contains
    'postmark' => [
      'token' => env('POSTMARK_TOKEN'),
      // specify the message stream that should be used by a given mailer
      'transport' => 'postmark',
      'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
    ],

    // --- Amazon SES
    composer require aws/aws-sdk-php
    // next, set the driver option in config/mail.php configuration file
    // to "ses" and verify that config/services.php configuration file
    // contains the following options:
    'ses' => [
      'key' => env('AWS_ACCESS_KEY_ID'),
      'secret' => env('AWS_SECRET_ACCESS_KEY'),
      'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
      // AWS temporary credentials via a session token:
      'token' => env('AWS_SESSION_TOKEN'),
    ],
    // include additional options when executing the SES SendRawEmail request,
    // define an options array within ses configuration:
    'ses' => [
      'key' => 'your-ses-key',
      'secret' => 'your-ses-secret',
      'region' => 'ses-region',  // e.g. us-east-1
      'options' => [
        'ConfigurationSetName' => 'MyConfigurationSet',
        'Tags' => [
          [ 'Name' => 'foo', 'Value' => 'bar' ],
        ],
      ],
    ],

    // --- Failover Configuration
    // when external service may be down
    'mailers' => [
      'failover' => [
        'transport' => 'failover',
        'mailers' => [
          'postmark',
          'mailgun',
          'sendmail',
        ],
      ],
      // ...
    ],
    // set it as default:
    'default' => env('MAIL_MAILER', 'failover'),
  

    namespace App\Mail;
    use Illuminate\Bus\Queueable;
    use Illuminate\Mail\Mailable;
    use Illuminate\Queue\SerializesModels;
    use Illuminate\Contracts\Queue\ShouldQueue;
    class EmailTests extends Mailable {
    // mailable class that should always be queued
    // even if you call send method when mailing,
    // mailable will still be queued since it implements the contract
    // class OrderShipped extends Mailable implements ShouldQueue {
      use Queueable, SerializesModels;
      // any defined public property will automatically be made available to the view:
      public $order; // order instance, {{ $order->price }} in template
      // create a new message instance
      public function __construct(Order $order) {
        // ...
      }
      // build the message
      public function build() {
        return $this
          // sender of the email
          ->from('example@example.com')
          ->view('emails.orders.shipped') // template
          // define a plain-text version of email
          ->text('emails.orders.shipped_plain')
          // set data to protected or private properties
          // so is not automatically made available to the template
          // then manually pass data to the view
          ->with([
            'orderName' => $this->order->name,
            'orderPrice' => $this->order->price, // {{ $orderPrice }}
          ])
          // add attachments
          ->attach('/path/to/file')
          // specify the display name and / or MIME type
          ->attach('/path/to/file', [
            'as' => 'name.pdf',
            'mime' => 'application/pdf',
          ])
          // file on one of filesystem disks
          ->attachFromStorage('/path/to/file')
          // with name and additional options
          ->attachFromStorage('/path/to/file', 'name.pdf', [
            'mime' => 'application/pdf'
          ])
          // specify a storage disk other than default disk
          ->attachFromStorageDisk('s3', '/path/to/file')
          // attach a raw string of bytes as an attachment
          // for generated PDF in memory and attachments without writing to disk
          ->attachData($this->pdf, 'name.pdf', [
            'mime' => 'application/pdf',
          ]);

          // register a callback which will be invoked with the raw Symfony mailer message
          // customize the message before it is delivered
          $this->withSymfonyMessage(function ($message) {
            $message->getHeaders()->addTextHeader(
              'Custom-Header', 'HeaderValue'
            );
          });

      }

      // capture the HTML content of a mailable without sending it
      // return evaluated contents of the mailable as a string
      public function render () {
        $invoice = App\Invoice::find(1);
        return (new App\Mail\InvoicePaid($invoice))->render();
      }

    }

    // --- config/mail.php - GLOBAL configurations
    'from' => ['address' => 'example@example.com', 'name' => 'App Name'],
    'reply_to' => ['address' => 'example@example.com', 'name' => 'App Name'],

    // --- embed - inline attachments
    // $message variable available to all email templates
    // not available in plain-text messages
    <body>
      Here is an image:
      <img src="{{ $message->embed($pathToImage) }}">
      // embedData - raw data string
      <img src="{{ $message->embedData($data, $name) }}">
    </body>

    // --- preview design without sending it to an actual email address
    Route::get('mailable', function () {
      $invoice = App\Invoice::find(1);
      return new App\Mail\InvoicePaid($invoice);
    });
  
Markdown Mailables

    // pre-built templates and components of mail notifications in mailables
    // responsive HTML templates for the messages
    // while also automatically generating a plain-text counterpart:
    // php artisan make:mail OrderShipped --markdown=emails.orders.shipped
    // then use markdown method instead of the view:
    // ...
      public function build() {
        return $this->from('example@example.com')
                    ->markdown('emails.orders.shipped');
      }
    // ...
    // combination of Blade components and Markdown syntax
    // do not use indentation when writing Markdown emails
    // Markdown parsers will render indented content as code blocks
    @component('mail::message')
    # Order Shipped
    order has been shipped!
    @component('mail::button', ['url' => $url])
    View Order
    @endcomponent
    Thanks,<br>
    {{ config('app.name') }}
    @endcomponent

    // --- button component - centered button link
    @component('mail::button', ['url' => $url, 'color' => 'success']) // primary, success, error
    View Order
    @endcomponent

    // --- panel component
    // block of text in a panel with different background color
    // draw attention to a given block of text
    @component('mail::panel')
    This is the panel content.
    @endcomponent

    // --- table component
    // transform a Markdown table into an HTML table
    // column alignment is supported using the default Markdown table alignment syntax
    @component('mail::table')
    | Laravel       | Table         | Example  |
    | ------------- |:-------------:| --------:|
    | Col 2 is      | Centered      | $10      |
    | Col 3 is      | Right-Aligned | $20      |
    @endcomponent

    // --- export all Markdown mail components to own application for customization
    // php artisan vendor:publish --tag=laravel-mail
    // will publish the Markdown mail components to the resources/views/vendor/mail
    // will contain an html and a text directory of every available component
    // resources/views/vendor/mail/html/themes will contain a default.css file
    // styles will automatically be in-lined within the HTML representations
    // to build an entirely new theme for the Markdown components,
    // write a new CSS file within the html/themes directory
    // and change the "theme" option of "mail" configuration file
  

    namespace App\Http\Controllers;
    use App\Order;
    use App\Mail\OrderShipped;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Mail;
    use App\Http\Controllers\Controller;
    class OrderController extends Controller {
      // ship the given order
      public function ship(Request $request, $orderId) {
        $order = Order::findOrFail($orderId);
        // ship order...
        // send email
        // accepts an email address, a user instance, or a collection of users
        Mail::to($request->user())->send(new OrderShipped($order));
        // if you pass an object or collection of objects,
        // mailer will automatically use their email and name properties
        // when setting the email recipients,
        // make sure these attributes are available on objects
      }
    }

    // --- set "to", "cc", and "bcc" recipients all within chained method call
    Mail::to($request->user())
        ->cc($moreUsers)
        ->bcc($evenMoreUsers)
        ->send(new OrderShipped($order));

    // --- queue a mail message
    // take care of pushing a job onto the queue so the message is sent in the background
    // configure queues before using this feature
    Mail::to($request->user())
        ->cc($moreUsers)
        ->bcc($evenMoreUsers)
        ->queue(new OrderShipped($order));

    // --- delay the delivery of a queued email message
    $when = now()->addMinutes(10);
    Mail::to($request->user())
        ->cc($moreUsers)
        ->bcc($evenMoreUsers)
        ->later($when, new OrderShipped($order));

    // --- onQueue , onConnection - specify the connection and queue name for the message
    $message = (new OrderShipped($order))
                    ->onConnection('sqs')
                    ->onQueue('emails');
    Mail::to($request->user())
            ->cc($moreUsers)
            ->bcc($evenMoreUsers)
            ->queue($message);

    // --- locale, other than the current language
    // will remember this locale if the mail is queued
    Mail::to($request->user())
      ->locale('es')
      ->send(new OrderShipped($order));

    // --- user preferred locales
    // instruct Laravel to use stored locale when sending mail
    use Illuminate\Contracts\Translation\HasLocalePreference;
    class User extends Model implements HasLocalePreference {
      // get the user preferred locale
      public function preferredLocale() {
        return $this->locale;
      }
    }
    // use preferred locale when sending mailables and notifications to the model
    // no need to call the locale method when using this interface
    Mail::to($request->user())->send(new OrderShipped($order));

    // --- MessageSending and MessageSent events
    // fired when the mail is being sent, not when it is queued
    // register an event listener for this event in EventServiceProvider
    protected $listen = [
      'Illuminate\Mail\Events\MessageSending' => [
        'App\Listeners\LogSendingMessage',
      ],
      'Illuminate\Mail\Events\MessageSent' => [
        'App\Listeners\LogSentMessage',
      ],
    ];

    // --- multiple mail drivers
    // configure multiple mailers for a single app within the mail configuration file
    // own options and even its own unique transport
    // use different email services to send certain email messages
    // send a message using a specific mailer configuration:
    Mail::mailer('postmark')
      ->to($request->user())
      ->send(new OrderShipped($order));
  

Queues


    // --- config/queue.php
    return [
      'default' => env('QUEUE_CONNECTION', 'sync'),
      // connection to a backend service
      'connections' => [
        'sync' => [
          'driver' => 'sync',
        ],
        'database' => [
          'driver' => 'database',
          'table' => env('QUEUE_TABLE', 'jobs'),
          'queue' => 'default', // queue that jobs will be dispatched to
          // how many seconds the queue connection should wait
          // before retrying a job that is being processed
          // set to maximum number of seconds jobs should reasonably take
          'retry_after' => 90,
        ],
        // ...
        'redis' => [
          'driver' => 'redis',
          'connection' => env('QUEUE_REDIS_CONNECTION', 'default'),
          'queue' => 'default',
          'retry_after' => 90,
          'block_for' => null,
        ],
      ],
      // behavior of failed queue job logging
      // control which database and table are used to store the jobs that have failed
      'failed' => [
        'database' => env('DB_CONNECTION', 'mysql'),
        'table' => env('QUEUE_FAILED_TABLE', 'failed_jobs'),
      ],
    ];
  
Drivers

    // --- to use database queue driver,
    // create a database table to hold the jobs
    php artisan queue:table
    php artisan migrate

    // --- Redis
    // configure a Redis database connection in config/database.php configuration file
    // - Redis Cluster
    // queue names must contain a key hash tag
    // required in order to ensure all of the Redis keys for a given queue
    // are placed into the same hash slot
    'redis' => [
      'driver' => 'redis',
      'connection' => 'default',
      'queue' => '{default}',
      'retry_after' => 90,
      // driver should block for five seconds while waiting for a job to become available
      'block_for' => 5,
      // wait until all open db transactions have been committed before dispatching the job
      // discard when transaction rolled back due to an exception
      // also cause any queued event listeners, mailables, notifications, and broadcast events
      // to be dispatched after all open database transactions have been committed
      'after_commit' => true,
    ],

    // --- listed queue drivers dependencies
    Amazon SQS: aws/aws-sdk-php ~3.0
    Beanstalkd: pda/pheanstalk ~4.0
    Redis: predis/predis ~1.0
  
Creating

    namespace App\Jobs;
    use App\Podcast;
    use App\AudioProcessor;
    use Illuminate\Bus\Queueable;
    use Illuminate\Queue\SerializesModels;
    use Illuminate\Queue\InteractsWithQueue;
    use Illuminate\Contracts\Queue\ShouldQueue;
    use Illuminate\Foundation\Bus\Dispatchable;
    class ProcessPodcast implements ShouldQueue {
      use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
      protected $podcast;
      // create a new job instance
      public function __construct(Podcast $podcast) {
        $this->podcast = $podcast;
      }
      // execute the job
      public function handle(AudioProcessor $processor) {
        // process uploaded podcast...
      }
      // --- specify connection as a property, alternative to onConnection
      public $connection = 'sqs';
      // or, Artisan command: php artisan queue:work --tries=3
      // alternative, time at which the job should timeout
      // allows a job to be attempted any number of times within a given time frame
      public function retryUntil() {
        return now()->addSeconds(5);
      }

      // delete the job if its models no longer exist
     public $deleteWhenMissingModels = true;

      // job was failed to process
      public function failed(Exception $exception) {
        // send user notification of failure, etc...
      }

      // --- RATE LIMITING

      // --- maximum number of attempts
      public $tries = 5;
      // --- maximum number of unhandled exceptions to allow before failing
      public $maxExceptions = 3;
      // --- number of seconds a job should be allowed to run on the job class itself
      // optimized for PHP 7.1+ and the pcntl PHP extension
      public $timeout = 120; // OR, Artisan command: php artisan queue:work --timeout=30
      public function handle() {
        // --- release - manually release job back onto the queue for later attempt
        this->release();
        this->release(10); // make job available for processing until seconds has elapsed
        // fail - mark a job as failed
        $this->fail();
        $this->fail($exception);

        // --- throttle - throttle queued jobs by time or concurrency
        // for application which interacts with Redis
        // throttle job to only run 10 times every 60 seconds
        // if a lock can not be obtained,
        // release the job back onto the queue so it can be retried later
        // key - any string that uniquely identifies the type of job to rate limit
        // releasing a throttled job back onto the queue
        // will still increment total number of attempts
        Redis::throttle('key')
          ->allow(10)
          ->every(60)
          ->then(function () {
            // job logic...
          }, function () {
            // could not obtain lock...
            return $this->release(10);
          });
        // --- funnel - maximum number of workers that may simultaneously process job
        // helpful when a queued job is modifying a resource
        // that should only be modified by one job at a time
        Redis::funnel('key')
          ->limit(1)
          ->then(function () {
            // job logic...
          }, function () {
            // could not obtain lock...
            return $this->release(10);
          });
        // combine rate limiting with time based attempts
        // to determine number of attempts correctly
      }

    }

    // --- SERVICE PROVIDER

    namespace App\Providers;
    use Illuminate\Support\ServiceProvider;
    use App\Jobs\ProcessPodcast;
    use Illuminate\Support\Facades\Queue;
    use Illuminate\Queue\Events\JobFailed;
    use Illuminate\Queue\Events\JobProcessed;
    use Illuminate\Queue\Events\JobProcessing;
    class AppServiceProvider extends ServiceProvider {
      // ...
      public function boot() {
        // --- bindMethod
        // take total control over how the container injects dependencies into the handle method
        // call this method from a service provider
        $this->app->bindMethod(
          ProcessPodcast::class.'@handle',
          function ($job, $app) { // callback which receives the job
            return $job->handle($app->make(AudioProcessor::class));
          }
        );
        // --- failing - register an event that will be called when a job fails
        // notify team via email or Slack
        Queue::failing(function (JobFailed $event) {
          // $event->connectionName
          // $event->job
          // $event->exception
        });
        // --- before , after
        // specify callbacks to be executed before or after a queued job is processed
        // perform additional logging or increment statistics for a dashboard
        Queue::before(function (JobProcessing $event) {
          // $event->connectionName
          // $event->job
          // $event->job->payload()
        });
        Queue::after(function (JobProcessed $event) {
          // $event->connectionName
          // $event->job
          // $event->job->payload()
        });
        // --- looping
        // specify callbacks executed before worker attempts to fetch a job from a queue
        // rollback any transactions that were left open by a previously failed job:
        Queue::looping(function () {
          while (DB::transactionLevel() > 0) {
            DB::rollBack();
          }
        });
      }
      // ...
    }
  
Dispatching

    namespace App\Http\Controllers;
    use App\Jobs\ProcessPodcast;
    use App\Models\Podcast;
    use Illuminate\Http\Request;
    use App\Http\Controllers\Controller;
    class PodcastController extends Controller {
      // store a new podcast
      public function store(Request $request) {
        $podcast = Podcast::create(...);

        // --- dispatch - send to the default queue, dispatch jobs from anywhere
        ProcessPodcast::dispatch($podcast);
        // conditionally dispatch a job
        ProcessPodcast::dispatchIf($accountActive, $podcast);
        ProcessPodcast::dispatchUnless($accountSuspended, $podcast);
        // OR, with facade, uncomment $app->withFacades() in bootstrap/app.php file:
        Queue::push(new ExampleJob);

        // --- delay - job should not be available for processing
        // until 10 minutes after it has been dispatched
        // Amazon SQS queue service has a maximum delay time of 15 minutes
        ProcessPodcast::dispatch($podcast)
                        ->delay(now()->addMinutes(10));

        // --- dispatchAfterResponse method delay dispatching until after the HTTP response is sent to browser
        // typically only for jobs that take about a second, such as sending an email
        // processed within the current HTTP request
        // do not require a queue worker to be running
        // use App\Jobs\SendNotification;
        SendNotification::dispatchAfterResponse();
        // chain the afterResponse method onto the dispatch helper
        // use App\Mail\WelcomeMessage;
        // use Illuminate\Support\Facades\Mail;
        dispatch(function () {
            Mail::to('taylor@example.com')->send(new WelcomeMessage);
        })->afterResponse();

        // --- dispatchSync - run job immediately within the current process
        // job will not be queued
        ProcessPodcast::dispatchSync($podcast);

        // specific job behavior on db transactions
        ProcessPodcast::dispatch($podcast)->afterCommit();
        ProcessPodcast::dispatch($podcast)->beforeCommit();

        // --- dispatch a Closure
        // for quick, simple tasks to be executed outside of the current request cycle
        // Closure code contents is cryptographically signed, can not be modified in transit
        $podcast = App\Podcast::find(1);
        dispatch(function () use ($podcast) {
          $podcast->publish();
        });

        // --- chain method, provided by the Bus facade
        // specify a list of queued jobs that should be run in sequence
        // if one job in the sequence fails, the rest of the jobs will not be run
        // use App\Jobs\OptimizePodcast;
        // use App\Jobs\ProcessPodcast;
        // use App\Jobs\ReleasePodcast;
        // use Illuminate\Support\Facades\Bus;
        Bus::chain([
            new ProcessPodcast,
            new OptimizePodcast,
            new ReleasePodcast,
        ])->dispatch();
        // chain closures
        Bus::chain([
            new ProcessPodcast,
            new OptimizePodcast,
            function () {
                Podcast::update(...);
            },
        ])->dispatch();
        // deleting jobs using the $this->delete() method
        // will not prevent chained jobs from being processed
        // chain will only stop executing if a job in the chain fails

        // --- onConnection/onQueue specify the queue connection and queue name that used
        // unless the queued job is explicitly assigned a different connection/queue
        Bus::chain([
            new ProcessPodcast,
            new OptimizePodcast,
            new ReleasePodcast,
        ])->onConnection('redis')->onQueue('podcasts')->dispatch();

        // --- catch - specify closure invoked if chain fails
        Bus::chain([
          new ProcessPodcast,
          new OptimizePodcast,
          new ReleasePodcast,
        ])->catch(function (Throwable $e) {
          // a job within the chain has failed...
        })->dispatch();

        // --- onQueue - pushing jobs to different queues
        // "categorize" queued jobs and even prioritize
        // how many workers you assign to various queues
        // this does not push jobs to different queue "connections"
        // as defined by queue configuration file,
        // but only to specific queues within a single connection
        ProcessPodcast::dispatch($podcast)->onQueue('processing');
        // --- onConnection - specify which connection to push a job to
        ProcessPodcast::dispatch($podcast)->onConnection('sqs');
        // specify the connection and the queue for a job
        ProcessPodcast::dispatch($podcast)
          ->onConnection('sqs')
          ->onQueue('processing');
        // --- retryUntil, also can be chained and used like in job class
      }
    }
  
Job Batching



  

Container/Provider

Container

    // --- make - resolve a class instance out of the container
    $api = $this->app->make('HelpSpot\API');
    $api = resolve('HelpSpot\API'); // when no access to the $app
    // --- makeWith - when class dependencies are not resolvable via the container
    $api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);

    $instance = app(Something::class);

    // --- resolving - listen to event fired each time it resolves an object
    $this->app->resolving(function ($object, $app) {
      // called when container resolves object of any type...
    });
    $this->app->resolving(HelpSpot\API::class, function ($api, $app) {
      // called when container resolves objects of type "HelpSpot\API"...
    });

    namespace App\Http\Controllers;
    use App\User;
    use App\Repositories\UserRepository;
    use App\Http\Controllers\Controller;
    class UserController extends Controller {
      // user repository implementation
      protected $users;
      // create a new controller instance
      // inject a service that is able to retrieve users
      // for queued jobs, type-hint dependencies in the handle method
      public function __construct(UserRepository $users) {
        $this->users = $users;
      }
      // show the profile for the given user
      public function show($id) {
        $user = $this->users->find($id);
        return view('user.profile', ['user' => $user]);
      }
    }

    // --- type-hint the PSR-11 container interface to obtain container instance
    use Psr\Container\ContainerInterface;
    // ...
    Route::get('/', function (ContainerInterface $container) {
      $service = $container->get('Service');
      // ...
    });
  
Provider

    namespace App\Providers;
    use Riak\Connection;
    use Illuminate\Support\ServiceProvider;
    use Illuminate\Contracts\Routing\ResponseFactory;
    class RiakServiceProvider extends ServiceProvider {
      // --- register
      // only bind things into the service container
      public function register() {

        // --- bind - register a binding, pass class or interface name
        $this->app->bind('HelpSpot\API', function ($app) {
          // received container itself as an argument to the resolver
          // use to resolve sub-dependencies of the object we are building
          return new HelpSpot\API($app->make('HttpClient'));
        });

        // --- singleton - bind class or interface that should only be resolved one time
        // once resolved, same object instance will be returned on subsequent calls
        $this->app->singleton(Connection::class, function ($app) {
          return new Connection(config('riak'));
        });
        $this->app->singleton('HelpSpot\API', function ($app) {
          return new HelpSpot\API($app->make('HttpClient'));
        });

        // --- instance - bind an existing object instance into the container
        // instance will always be returned on subsequent calls into the container
        $api = new HelpSpot\API(new HttpClient); // ...
        $this->app->instance('HelpSpot\API', $api);

        // --- contextual binding - inject any value class may need
        // when class that receives some injected classes, also needs an injected primitive
        $this->app->when('App\Http\Controllers\UserController')
                  ->needs('$variableName')
                  ->give($value);
        // inject different implementations, for classes that utilize the same interface
        $this->app->when(PhotoController::class)
                  ->needs(Filesystem::class)
                  ->give(function () {
                    return Storage::disk('local');
                  });
        $this->app->when([VideoController::class, UploadController::class])
                  ->needs(Filesystem::class)
                  ->give(function () {
                    return Storage::disk('s3');
                  });

        // --- tag - resolve all of a certain "category" of binding via the tagged method
        $this->app->bind('SpeedReport', function () { /* ... */ });
        $this->app->bind('MemoryReport', function () { /* ... */ });
        $this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');
        $this->app->bind('ReportAggregator', function ($app) {
          return new ReportAggregator($app->tagged('reports'));
        });

        // --- extend - modification of resolved services
        // run additional code to decorate or configure the service
        $this->app->extend(Service::class, function ($service) {
          return new DecoratedService($service);
        });

      }

      // --- bootstrap any application services
      public function boot() {
        view()->composer('view', function () {
          // ...
        });
      }
      // type-hint dependencies for service provider boot method
      // service container will automatically inject any dependencies need:
      public function boot(ResponseFactory $response) {
        $response->macro('caps', function ($value) {
          // ...
        });
      }

      // --- bindings , singletons
      // for many simple bindings, instead of manual registration
      // when the service provider is loaded by the framework,
      // automatically check for these properties and register their bindings
      public $bindings = [ // bindings that should be registered
        ServerProvider::class => DigitalOceanServerProvider::class,
      ];
      public $singletons = [ // singletons that should be registered
        DowntimeNotifier::class => PingdomDowntimeNotifier::class,
        ServerToolsProvider::class => ServerToolsProvider::class,
      ];
    }
  
Deferred Provider

    namespace App\Providers;
    use Riak\Connection;
    use Illuminate\Support\ServiceProvider;
    use Illuminate\Contracts\Support\DeferrableProvider;
    class RiakServiceProvider extends ServiceProvider implements DeferrableProvider {
      // register any application services
      public function register() {
        $this->app->singleton(Connection::class, function ($app) {
          return new Connection($app['config']['riak']);
        });
      }
      // get the services provided by the provider
      public function provides() {
        // return the service container bindings registered by the provider
        return [Connection::class];
      }
    }
  

    // --- config/app.php
    'providers' => [
      // other service providers...
      App\Providers\ComposerServiceProvider::class,
    ],
  

Testing


    // namespace Tests\Feature;
    use Tests\TestCase;
    use Laravel\Lumen\Testing\DatabaseMigrations;
    use Laravel\Lumen\Testing\DatabaseTransactions;
    use Illuminate\Foundation\Testing\RefreshDatabase;
    use Illuminate\Http\UploadedFile;
    use Illuminate\Support\Facades\Storage;
    use Illuminate\Foundation\Testing\WithoutMiddleware;

    class ExampleTest extends TestCase {
      use DatabaseMigrations; // rollback db after each test and migrate it before the next test
      use DatabaseTransactions; //  wrap every test case in a db transaction
      use RefreshDatabase; // reset db after each tests

      $this->seed(); // run DatabaseSeeder
      $this->seed(OrderStatusSeeder::class); // run specific seeder
      // OR instruct RefreshDatabase trait to automatically seed the db before each test
      protected $seed = true;
      protected $seeder = OrderStatusSeeder::class;

      public function testSomethingIsTrue() {
        $this->assertTrue(true);

        // modify the time returned by helpers
        // such as 'now' or Illuminate\Support\Carbon::now()
        // travel into the future...
        $this->travel(5)->milliseconds();
        // ...
        $this->travel(5)->years();
        // travel into the past
        $this->travel(-5)->hours();
        // travel to an explicit time
        $this->travelTo(now()->subHours(6));
        // return back to the present time
        $this->travelBack();
      }
      public function testHttpExample() {
        // --- get, post, put, patch, delete
        $response = $this->get('/');
        $response->assertStatus(200);
        // --- withHeaders - add any custom headers to the request
        $response = $this->withHeaders([
            'X-Header' => 'Value',
        ])->json('POST', '/user', ['name' => 'Sally']);
        // --- dump , dumpHeaders - examine and debug the response contents
        $response->dumpHeaders();
        $response->dump();
        // json --- assert that a given array was returned in JSON format
        // assertJson , seeJson --- convert given array into JSON,
        // then verifies that the JSON fragment occurs anywhere
        // within the entire JSON response returned by the application
        // if there are other properties in the JSON response,
        // test will still pass as long as the given fragment is present
        $this->json('POST', '/user', ['name' => 'Sally'])
              ->assertStatus(201)
              ->assertJson([
              // ->seeJson([
                'created' => true,
              ]);
        // --- assertExactJson  , seeJsonEquals - exact match for the returned JSON
        $this->post('/user', ['name' => 'Sally'])
              // ->seeJsonEquals([
              ->assertExactJson([
                'created' => true,
              ]);
        // --- withSession - loading the session with data before issuing a request
        $response = $this->withSession(['foo' => 'bar'])
                         ->get('/');
        // --- actingAs - authenticate a given user as the current user
        $user = factory('App\User')->create();
        $this->actingAs($user)->get('/user');
        // specify which guard should be used to authenticate
        $this->actingAs($user, 'api')
        $response = $this->actingAs($user)
                          ->withSession(['foo' => 'bar'])
                          ->get('/');
        // --- call - custom HTTP request into app and full Illuminate\Http\Response object
        $response = $this->call('GET', '/');
        $this->assertEquals(200, $response->status());
        // pass an array of input data with the request
        // will be available in routes and controller via the Request instance
        $response = $this->call('POST', '/user', ['name' => 'Taylor']);
      }

      public function testWithAuthExample() {
        // user is authenticated
        $this->assertAuthenticated($guard = null);
        // user is not authenticated
        $this->assertGuest($guard = null);
        // user is authenticated
        $this->assertAuthenticatedAs($user, $guard = null);
        // redentials are valid
        $this->assertCredentials(array $credentials, $guard = null);
        // given credentials are invalid
        $this->assertInvalidCredentials(array $credentials, $guard = null);
      }

      public function testDbExample() {

        // --- make - create model with factory but dont save to database
        $user = factory('App\User')->make();
        // replace default values
        $user = factory('App\User')->make([
          'name' => 'Abigail',
        ]);
        $users = factory('App\User', 3)->make(); // three App\User instances
        $user = factory('App\User', 'admin')->make(); // an App\User "admin" instance
        $users = factory('App\User', 'admin', 3)->make(); // three App\User "admin" instances
        // --- states - apply states
        $users = factory(App\User::class, 5)->states('delinquent')->make();
        $users = factory(App\User::class, 5)->states('premium', 'delinquent')->make();

        // --- create - create model and save to the database using Eloquent save
        $user = factory('App\User')->create();
        $users = factory(App\User::class, 3)->create();
        $user = factory(App\User::class)->create([
          'name' => 'Abigail',
        ]);

        // --- relationships
        // persist multiple models to the database
        // create method returns Eloquent collection instance
        // attach a relation to the created models
        // use any of the convenient functions provided by the collection, such as each
        $users = factory('App\User', 3)
                ->create()
                ->each(function($u) {
                  $u->posts()->save(factory('App\Post')->make());
                });

        // --- assertDatabaseCount
        // assert that a table in the db contains the given number of records
        $this->assertDatabaseCount('users', 5);
        // --- assertDatabaseHas , seeInDatabase
        // data exists in the db matching a given set of criteria
        $this->seeInDatabase('users', ['email' => 'sally@foo.com']);
        // --- table in the db does not contain the given data
        $this->assertDatabaseMissing($table, array $data);
        // --- assertModelMissing, model has been deleted from the database:
        // use App\Models\User;
        $user = User::find(1);
        $user->delete();
        $this->assertModelMissing($user);
        // --- record has been soft deleted
        $this->assertSoftDeleted($table, array $data);

        // use models in tests...
      }

    }
  
HTTP Asserts

    // --- JSON
    // response contains an exact match of the given JSON data
    // requires numeric keys of compared arrays to match and be in the same order
    $response->assertExactJson(array $data);
    // assertSimilarJson - without requiring numerically keyed arrays to have the same order
    // response contains the given JSON data
    $response->assertJson(array $data);
    // response JSON has an array with the expected number of items at the given key
    $response->assertJsonCount($count, $key = null);
    // response contains the given JSON fragment
    $response->assertJsonFragment(array $data);
    // response does not contain the given JSON fragment
    $response->assertJsonMissing(array $data);
    // response does not contain the exact JSON fragment
    $response->assertJsonMissingExact(array $data);
    // response has no JSON validation errors for the given keys
    $response->assertJsonMissingValidationErrors($keys);
    // response has a given JSON structure
    $response->assertJsonStructure(array $structure);
    // response has the given JSON validation errors
    $response->assertJsonValidationErrors(array $data);

    // --- STATUS
    // response has a given code
    $response->assertStatus($code);
    // response has a forbidden status code
    $response->assertForbidden();
    // response has a not found status code
    $response->assertNotFound();
    // response has a 200 status code
    $response->assertOk();
    // response has a successful status code
    $response->assertSuccessful();

    // --- HEADER , URI
    // header is present on the response
    $response->assertHeader($headerName, $value = null);
    // header is not present on the response
    $response->assertHeaderMissing($headerName);
    // response has the given URI value in the Location header
    $response->assertLocation($uri);
    // response is a redirect to a given URI
    $response->assertRedirect($uri);

    // --- STRING
    // string is contained within the response
    $response->assertSee($value, $escape_value = true);
    // strings are contained in order within the response
    $response->assertSeeInOrder(array $values);
    // string is contained within the response text
    $response->assertSeeText($value);
    // strings are contained in order within the response text
    $response->assertSeeTextInOrder(array $values);
    // string is not contained within the response
    $response->assertDontSee($value);
    // string is not contained within the response text
    $response->assertDontSeeText($value);

    // --- VIEW
    // response view was given a piece of data
    $response->assertViewHas($key, $value = null);
    // response view has a given list of data
    $response->assertViewHasAll(array $data);
    // given view was returned by the route
    $response->assertViewIs($value);
    // response view is missing a piece of bound data
    $response->assertViewMissing($key);

    // --- COOKIE
    // response contains the given cookie
    $response->assertCookie($cookieName, $value = null);
    // response contains the given cookie and it is expired
    $response->assertCookieExpired($cookieName);
    // response contains the given cookie and it is not expired
    $response->assertCookieNotExpired($cookieName);
    // response does not contains the given cookie
    $response->assertCookieMissing($cookieName);
    // response contains the given cookie (unencrypted)
    $response->assertPlainCookie($cookieName, $value = null);

    // --- SESSION
    // session contains the given piece of data
    $response->assertSessionHas($key, $value = null);
    // session has a given list of values
    $response->assertSessionHasAll(array $data);
    // session contains an error for the given field
    $response->assertSessionHasErrors(array $keys, $format = null, $errorBag = 'default');
    // session has the given errors
    $response->assertSessionHasErrorsIn($errorBag, $keys = [], $format = null);
    // session has no errors
    $response->assertSessionHasNoErrors();
    // session has no errors for the given keys
    $response->assertSessionDoesntHaveErrors($keys = [], $format = null, $errorBag = 'default');
    // session does not contain the given key
    $response->assertSessionMissing($key);
  
Mocking, faking

    // --- expectsEvents - prevents expected events running any handlers
    $this->expectsEvents('App\Events\UserRegistered');

    // --- withoutEvents - prevent all event handlers from running
    $this->withoutEvents();

    // --- expectsJobs - dispatch but dont execute expected jobs
    // only detects jobs that are dispatched via the dispatch functions
    // does not detect jobs that are sent directly to Queue::push
    $this->expectsJobs('App\Jobs\PurchasePodcast');

    // --- shouldReceive - mock the call to the facade
    // dont mock the Request facade, pass the desired input into the HTTP helper
    // such as call and post when running test
    Cache::shouldReceive('get')
          ->once()
          ->with('key')
          ->andReturn('value');
    $this->get('/users');

    // --- mocking objects
    // when mocking an object that is going to be injected into application via service container,
    // bind mocked instance into the container as an instance binding,
    // will instruct the container to use mocked instance of the object
    // instead of constructing the object itself:
    use Mockery;
    use App\Service;
    // ...
    $this->instance(Service::class, Mockery::mock(Service::class, function ($mock) {
      $mock->shouldReceive('process')->once();
    }));
    // --- mock - for convenience
    use App\Service;
    // ...
    $this->mock(Service::class, function ($mock) {
      $mock->shouldReceive('process')->once();
    });

    // --- Bus::fake() - alternative to mocking, prevent jobs from being dispatched
    // use Illuminate\Support\Facades\Bus;
    Bus::fake();
    // ...perform order shipping...
    Bus::assertDispatched(ShipOrder::class, function ($job) use ($order) {
      return $job->order->id === $order->id;
    });
    // job was not dispatched
    Bus::assertNotDispatched(AnotherJob::class);

    // --- Storage::fake - Illuminate\Http\UploadedFile class method
    // used to generate dummy files or images for testing
    // combine to test an avatar upload form
    // use Illuminate\Support\Facades\Storage;
    Storage::fake('avatars');
    $file = UploadedFile::fake()->image('avatar.jpg');
    // specify the width, height, and size of the image
    // to better test validation rules
    $file = UploadedFile::fake()->image('avatar.jpg', $width, $height)->size(100);
    // create files of any other type
    $file = UploadedFile::fake()->create('document.pdf', $sizeInKilobytes);
    $response = $this->json('POST', '/avatar', [
      'avatar' => $file,
    ]);
    Storage::fake('photos');
    $response = $this->json('POST', '/photos', [
      UploadedFile::fake()->image('photo1.jpg'),
      UploadedFile::fake()->image('photo2.jpg')
    ]);
    // --- assertExists - file was stored
    Storage::disk('photos')->assertExists('photo1.jpg');
    Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']);
    // --- assertMissing - file does not exist
    Storage::disk('photos')->assertMissing('missing.jpg');
    Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']);
    // --- persistentFake - dont delete files from temporary directory (like fake())

    // --- Mail::fake() - prevent mail from being sent, as testOrderShipping()
    // assert that mailables were sent to users and inspect the data they received
    // use Illuminate\Support\Facades\Mail;
    Mail::fake();
    // no mailables were sent
    Mail::assertNothingSent();
    // ...perform order shipping...
    Mail::assertSent(OrderShipped::class, function ($mail) use ($order) {
      return $mail->order->id === $order->id;
    });
    // message was sent to the given users
    Mail::assertSent(OrderShipped::class, function ($mail) use ($user) {
        return $mail->hasTo($user->email) &&
                $mail->hasCc('...') &&
                $mail->hasBcc('...');
    });
    // mailable was sent twice...
    Mail::assertSent(OrderShipped::class, 2);
    // mailable was not sent...
    Mail::assertNotSent(AnotherMailable::class);
    // if queueing mailables for delivery in the background,
    // use the assertQueued method instead of assertSent:
    Mail::assertQueued(...);
    Mail::assertNotQueued(...);

    // --- Event::fake() - prevent all event listeners from executing
    // assert that events were dispatched and inspect the data they received
    // use Illuminate\Support\Facades\Event;
    Event::fake();
    // ...perform order shipping...
    Event::assertDispatched(OrderShipped::class, function ($e) use ($order) {
      return $e->order->id === $order->id;
    });
    // event was dispatched twice
    Event::assertDispatched(OrderShipped::class, 2);
    // event was not dispatched
    Event::assertNotDispatched(OrderFailedToShip::class);
    // after calling Event::fake(), no event listeners will be executed
    // if tests use model factories that rely on events,
    // such as creating a UUID during a model creating event,
    // call Event::fake() after using factories
    // --- fake event listeners for a specific set of event
    Event::fake([
      OrderCreated::class,
    ]);
    $order = factory(Order::class)->create();
    Event::assertDispatched(OrderCreated::class);
    // events are dispatched as normal...
    $order->update([...]);
    // --- Event::fakeFor - scoped Event Fakes
    // fake event listeners for a portion of test
    $order = Event::fakeFor(function () {
      $order = factory(Order::class)->create();
      Event::assertDispatched(OrderCreated::class);
      return $order;
    });
    // events are dispatched as normal and observers will run
    $order->update([...]);

    // --- Notification::fake() - prevent notifications from being sent, as testOrderShipping()
    // assert that notifications were sent to users and inspect the data they received
    // use Illuminate\Support\Facades\Notification;
    Notification::fake();
    // Assert that no notifications were sent...
    Notification::assertNothingSent();
    // ...perform order shipping...
    Notification::assertSentTo(
      $user,
      OrderShipped::class,
      function ($notification, $channels) use ($order) {
        return $notification->order->id === $order->id;
      }
    );
    // notification was sent to the given users
    Notification::assertSentTo(
      [$user], OrderShipped::class
    );
    // notification was not sent
    Notification::assertNotSentTo(
      [$user], AnotherNotification::class
    );
    // notification was sent via Notification::route() method
    Notification::assertSentTo(
      new AnonymousNotifiable, OrderShipped::class
    );

    // --- Queue::fake() - prevent jobs from being queued, as testOrderShipping()
    // assert that jobs were pushed to the queue and inspect the data they received
    // use Illuminate\Support\Facades\Queue;
    Queue::fake();
    // no jobs were pushed
    Queue::assertNothingPushed();
    // ...perform order shipping...
    Queue::assertPushed(ShipOrder::class, function ($job) use ($order) {
      return $job->order->id === $order->id;
    });
    // job was pushed to a given queue
    Queue::assertPushedOn('queue-name', ShipOrder::class);
    // job was pushed twice
    Queue::assertPushed(ShipOrder::class, 2);
    // job was not pushed
    Queue::assertNotPushed(AnotherJob::class);
    // job was pushed with a specific chain...
    Queue::assertPushedWithChain(ShipOrder::class, [
        AnotherJob::class,
        FinalJob::class
    ]);
  
Model Factories (Laravel <=7)

    //app/database/factories/ModelFactory.php

    $factory->define(App\User::class, function (Faker\Generator $faker) {
      return [
        'name' => $faker->name,
        'email' => $faker->email,
      ];
    });

    // ---defineAs - define additional factories
    // "Administrator" users in addition to normal users
    $factory->defineAs('App\User', 'admin', function ($faker) {
      return [
        'name' => $faker->name,
        'email' => $faker->email,
        'admin' => true,
      ];
    });

    // --- raw - retrieve the base attributes
    // instead of duplicating all of the attributes from base user factory
    $factory->defineAs('App\User', 'admin', function ($faker) use ($factory) {
      $user = $factory->raw('App\User');
      return array_merge($user, ['admin' => true]);
    });

    // --- state
    // define modifications that can be applied to model factories in any combination
    $factory->state(App\User::class, 'delinquent', [
      'account_status' => 'delinquent',
    ]);
    $factory->state(App\User::class, 'address', function ($faker) {
      return [
        'address' => $faker->address,
      ];
    });

    // --- afterMaking , afterCreating
    // perform additional tasks after making or creating a model
    $factory->afterMaking(App\User::class, function ($user, $faker) {
      // ...
    });
    $factory->afterCreating(App\User::class, function ($user, $faker) {
      $user->accounts()->save(factory(App\Account::class)->make());
    });
    // callbacks for factory states
    $factory->afterMakingState(App\User::class, 'delinquent', function ($user, $faker) {
      // ...
    });
    $factory->afterCreatingState(App\User::class, 'delinquent', function ($user, $faker) {
      // ...
    });

    // --- relationships
    // create a new User instance when creating a Post
    $factory->define(App\Post::class, function ($faker) {
      return [
        'title' => $faker->title,
        'content' => $faker->paragraph,
        'user_id' => function () {
          return factory(App\User::class)->create()->id;
        }
      ];
    });
    // receive the evaluated attribute array of the factory that defines them
    $factory->define(App\Post::class, function ($faker) {
      return [
        'title' => $faker->title,
        'content' => $faker->paragraph,
        'user_id' => function () {
          return factory(App\User::class)->create()->id;
        },
        'user_type' => function (array $post) {
          return App\User::find($post['user_id'])->type;
        }
      ];
    });
  
Model Factories (Laravel 8+)
--- RELATIONSHIPS

    // --- hasMany - user that has three posts
    // Laravel will assume that User model must have a 'posts' method that defines the relationship
    use App\Models\Post;
    use App\Models\User;
      // ...
      $user = User::factory()
        ->has(Post::factory()->count(3))
        ->create();
    // explicitly specify the name of the relationship to manipulate
    $user = User::factory()
      ->has(Post::factory()->count(3), 'posts')
      ->create();
    // state manipulations on the related models
    $user = User::factory()
      ->has(
        Post::factory()
          ->count(3)
          ->state(function (array $attributes, User $user) {
              return ['user_type' => $user->type];
          })
      )->create();
    // - magic methods, use convention to determine related models relationship method
    $user = User::factory()
      ->hasPosts(3) // related models created via 'posts' relationship method on User model
      ->create();
    $user = User::factory()
      ->hasPosts(3, [
          'published' => false,
      ])->create();
    $user = User::factory()
      ->hasPosts(3, function (array $attributes, User $user) {
          return ['user_type' => $user->type];
      })->create();

    // --- belongsTo, inverse of hasMany
    use App\Models\Post;
    use App\Models\User;
      // ...
      $posts = Post::factory()
        ->count(3)
        ->for(User::factory()->state([
            'name' => 'Jessica Archer',
        ]))->create();
    // pass parent model instance
    $user = User::factory()->create();
    $posts = Post::factory()
      ->count(3)
      ->for($user)
      ->create();
    // - magic methods, use convention to determine related models relationship method
    $posts = Post::factory()
      ->count(3)
      ->forUser([ // posts should belong to the user relationship on the Post model
          'name' => 'Jessica Archer',
      ])->create();

    // --- belongsToMany, many to many
    use App\Models\Role;
    use App\Models\User;
      // ...
      $user = User::factory()
        ->has(Role::factory()->count(3))
        ->create();
    // attributes set on pivot/intermediate table linking the models
    $user = User::factory()
      ->hasAttached(
          Role::factory()->count(3),
          ['active' => true]
      )->create();
    // state transformation
    $user = User::factory()
      ->hasAttached(
        Role::factory()
          ->count(3)
          ->state(function (array $attributes, User $user) {
            return ['name' => $user->name.' Role'];
          }),
        ['active' => true]
      )->create();
    // three roles will be attached to all three users
    $roles = Role::factory()->count(3)->create();
    $user = User::factory()
                ->count(3)
                ->hasAttached($roles, ['active' => true])
                ->create();
    // - magic methods, use convention to determine related models relationship method
    $user = User::factory()
      ->hasRoles(1, [
        'name' => 'Editor'
      ])
      ->create();
  
--- RELATIONSHIPS WITHIN FACTORIES

    // assign a new factory instance to foreign key of relationship
    // normally done for the 'inverse' relationships: belongsTo and morphTo
    // create new user when creating post:
    use App\Models\User;
      // ...
      public function definition() {
        return [
          'user_id' => User::factory(),
          'title' => $this->faker->title(),
          'content' => $this->faker->paragraph(),
        ];
      }
    // if the relationship columns depend on factory that defines it, assign closure to attribute
    // closure will receive the factory evaluated attribute array
    public function definition() {
      return [
          'user_id' => User::factory(),
          'user_type' => function (array $attributes) {
              return User::find($attributes['user_id'])->type;
          },
          'title' => $this->faker->title(),
          'content' => $this->faker->paragraph(),
      ];
    }
  
--- POLYMORPHIC RELATIONSHIPS

    // 'morph many' relationships are created same way as typical hasMany relationships
    use App\Models\Post;
      // ...
      $post = Post::factory()->hasComments(3)->create(); // Post has morphMany with Comment

    // --- morphTo, NO magic methods
    // 'for' method must be used directly and relationship name must be explicitly provided
    // Comment has 'commentable' method that defines a morphTo relationship
    // create three comments that belong to single post by using the for method directly:
    $comments = Comment::factory()->count(3)->for(
      Post::factory(), 'commentable'
    )->create();

    // --- morphToMany, morphedByMany - created like non-polymorphic belongsToMany
    use App\Models\Tag;
    use App\Models\Video;
      // ...
      $videos = Video::factory()
        ->hasAttached(
          Tag::factory()->count(3),
          ['public' => true]
        )
        ->create();
    // magic methods, use convention to determine related models relationship method
    $videos = Video::factory()
      ->hasTags(3, ['public' => true])
      ->create();
  
Console Tests

    // --- expectsQuestion - "mock" user input for console commands
    // --- assertExitCode - specify the exit code and expected text from console command
    Artisan::command('question', function () {
      $name = $this->ask('What is your name?');
      $language = $this->choice('Which language do you prefer?', [
        'PHP',
        'Ruby',
        'Python',
      ]);
      $this->line('Your name is '.$name.' and you prefer '.$language.'.');
    });
    // --- expectsQuestion , expectsOutput , assertExitCode - test command
    // test a console command
    public function test_console_command() {
      $this->artisan('question')
        ->expectsQuestion('What is your name?', 'Taylor Otwell')
        ->expectsQuestion('Which language do you prefer?', 'PHP')
        ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.')
        ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.')
        ->assertExitCode(0);
    }
    // --- expectsConfirmation - command which expects confirmation in the form of a "yes" or "no" answer
      $this->artisan('module:import')
        ->expectsConfirmation('Do you really wish to run this command?', 'no')
        ->assertExitCode(1);
    // --- expectsTable
    $this->artisan('users:all')
      ->expectsTable([
          'ID',
          'Email',
      ], [
          [1, 'taylor@example.com'],
          [2, 'abigail@example.com'],
      ]);
  

Validation


    Route::get('post/create', 'PostController@create');
    Route::post('post', 'PostController@store');
    // ...
    namespace App\Http\Controllers;
    use Illuminate\Http\Request;
    use App\Http\Controllers\Controller;
    use Illuminate\Support\Facades\Validator;
    class PostController extends Controller {
      // show the form to create a new blog post
      public function create() {
        return view('post.create');
      }
      // store a new blog post
      public function store(Request $request) {
        $validatedData = $request->validate([
          'title' => 'required|unique:posts|max:255',
          'body' => 'required',
        ]);
        // --- bail
        // stop running validation rules after the first validation failure
        // if any rule fails, next will not be checked
        $request->validate([
          'title' => 'bail|required|unique:posts|max:255',
          'body' => 'required',
        ]);
        // --- mark "optional" request fields as nullable
        // null values are considered invalid
        // by default, Laravel includes TrimStrings and ConvertEmptyStringsToNull middleware
        // in application global middleware stack
        $request->validate([
          'title' => 'required|unique:posts|max:255',
          'body' => 'required',
          'publish_at' => 'nullable|date',
        ]);

        // --- "dot" syntax on nested parameters
        $request->validate([
          'title' => 'required|unique:posts|max:255',
          'author.name' => 'required',
          'author.description' => 'required',
        ]);
        // validate attributes within an array
        $validator = Validator::make($request->all(), [
          'photos.profile' => 'required|image',
        ]);
        // * - validate each element of an array
        $validator = Validator::make($request->all(), [
          'person.*.email' => 'email|unique:users',
          'person.*.first_name' => 'required_with:person.*.last_name',
        ]);

        // --- make - custom validator instance with Validator facade
        // instead of validate method on the request
        $validator = Validator::make(
          $request->all(), // data under validation
          [ // validation rules
            'title' => 'required|unique:posts|max:255',
            'body' => 'required',
          ]
        );
        if ($validator->fails()) {
          return redirect('post/create')
                // withErrors - accepts a validator, a MessageBag, or a PHP array
                ->withErrors($validator) // errors variable will automatically be shared
                ->withInput();
        }
        // --- validate
        // automatic redirection for manually created validator instance
        Validator::make($request->all(), [
          'title' => 'required|unique:posts|max:255',
          'body' => 'required',
        ])->validate();

        // --- MessageBag - for multiple forms on a single page
        // retrieve the error messages for a specific form
        return redirect('register')
                ->withErrors($validator, 'login');
        // access named MessageBag instance from the $errors variable:
        // {{ $errors->login->first('email') }}

        // --- after - attach callbacks to be run after validation is completed
        // further validation and add more error messages to the message collection
        $validator = Validator::make(...);
        $validator->after(function ($validator) {
          if ($this->somethingElseIsInvalid()) {
            $validator->errors()->add('field', 'Something is wrong with this field!');
          }
        });
        if ($validator->fails()) { /* ... */ }

        // --- CONDITIONAL VALIDATION

        // --- sometimes - checks against a field only if that field is present in the input array
        $v = Validator::make($data, [
          'email' => 'sometimes|required|email',
        ]);
        // adding rule depending on other fields
        // reason field is required when games > 100
        $v = Validator::make($data, [ // static rules that never change
          'email' => 'required|email',
          'games' => 'required|numeric',
        ]);
        $v->sometimes('reason', 'required|max:500', function ($input) {
          return $input->games >= 100;
        });
        // for several fields at once
        $v->sometimes(['reason', 'cost'], 'required', function ($input) {
          return $input->games >= 100;
        });

        // --- ERROR MESSAGES

        // --- first - first error message for a given field
        $errors = $validator->errors();
        echo $errors->first('email');
        // --- get - array of all the messages for a given field
        foreach ($errors->get('email') as $message) {
          // ...
        }
        // --- * - all of the messages for each of the array elements
        // when validating an array form field
        foreach ($errors->get('attachments.*') as $message) {
          // ...
        }
        // --- all - array of all messages for all fields
        foreach ($errors->all() as $message) {
          // ...
        }
        // --- has - determine if any error messages exist for a given field
        if ($errors->has('email')) {
          // ...
        }
        // --- custom error messages
        $messages = [
          // :attribute placeholder is replaced by the name of the field under validation
          'required' => 'The :attribute field is required.',
          // other placeholders in validation messages
          'same'    => 'The :attribute and :other must match.',
          'size'    => 'The :attribute must be exactly :size.',
          'between' => 'The :attribute value :input is not between :min - :max.',
          'in'      => 'The :attribute must be one of the following types: :values',
          // custom error message only for a specific field: attribute_name.rule
          'email.required' => 'We need to know your e-mail address!',
        ];
        $validator = Validator::make($input, $rules, $messages);

        // store the blog post...
      }
    }

    // --- in Lumen routes
    use Illuminate\Http\Request;
    $router->post('/user', function (Request $request) {
      $this->validate($request, [
        'name' => 'required',
        'email' => 'required|email|unique:users'
      ]);
      // Store User...
    });
  

    <!--
      user will be redirected to controller "create" method when validation fails,
      display the error messages in the view
      /resources/views/post/create.blade.php
    -->
    <h1>Create Post</h1>
    @if ($errors->any())
      <div class="alert alert-danger">
        <ul>
          @foreach ($errors->all() as $error)
            <li>{{ $error }}</li>
          @endforeach
        </ul>
      </div>
    @endif
    <!--
      ... create post form ...
    -->
    <!--
      @error Blade directive
      quickly check if validation error messages exist for a given attribute
    -->
    <label for="title">Post Title</label>
    <input id="title" type="text" class="@error('title') is-invalid @enderror">
    @error('title')
      <div class="alert alert-danger">{{ $message }}</div>
    @enderror
  
Form Request Validation (Laravel)

    // type-hint any dependencies within rules method signature
    // they will automatically be resolved via the Laravel service container
    public function rules() {
      return [
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
      ];
    }

    // type-hint the request on controller method
    // incoming form request is validated before the controller method is called
    // no need to clutter controller with any validation logic
    public function store(StoreBlogPost $request) {
      // incoming request is valid...
      // retrieve the validated input data...
      $validated = $request->validated();
    }

    // --- withValidator method - add an "after" hook to a form request
    // receives fully constructed validator,
    // call any of its methods before the validation rules are actually evaluated
    public function withValidator($validator) {
      $validator->after(function ($validator) {
        if ($this->somethingElseIsInvalid()) {
          $validator->errors()->add(
            'field',
            'Something is wrong with this field!'
          );
        }
      });
    }

    // --- authorize method
    // check if the authenticated user actually has the authority to update resource
    // if returns false, 403 status code returned and controller method will not execute
    // determine if a user actually owns a blog comment they are attempting to update
    public function authorize() {
      $comment = Comment::find($this->route('comment'));
      return $comment && $this->user()->can('update', $comment);
      // if you have authorization logic in another part of your application:
      return true;
    }
    // user() method also grants access to the URI parameters defined on the route
    // such as the {comment} parameter: Route::post('comment/{comment}');

    // --- messages method
    // customize the error messages used by the form request
    // should return an array of attribute/rule pairs
    // and their corresponding error messages
    public function messages() {
      return [
        'title.required' => 'A title is required',
        'body.required'  => 'A message is required',
      ];
    }

    // --- attributes method
    // replace :attribute portion of validation message with a custom attribute name
    public function attributes() {
      return [
        'email' => 'email address',
      ];
    }
  
lang/xx/validation.php

    // --- custom array - specify custom messages in a language file instead of
    // passing them directly to the Validator
    'custom' => [
      'email' => [
        'required' => 'We need to know your e-mail address!',
      ],
    ],

    // --- attributes array - custom attributes in language files
    // replace :attribute portion of validation message with a custom attribute name
    'attributes' => [
      'email' => 'email address',
    ],

    // --- values array - custom value representation
    'values' => [
      'payment_type' => [
        'cc' => 'credit card'
      ],
    ],
    // then, for:
    $request->validate([
      'credit_card_number' => 'required_if:payment_type,cc'
    ]);
    // message will be:
    The credit card number field is required when payment type is credit card.
    // instead of:
    The credit card number field is required when payment type is cc.

    // --- * - use a single validation message for array based fields
    'custom' => [
      'person.*.email' => [
        'unique' => 'Each person must have a unique e-mail address',
      ]
    ],
  
Validation Rules

    boolean // able to be cast as a boolean: true, false, 1, 0, "1", and "0"
    alpha // entirely alphabetic characters
    alpha_dash // may have alpha-numeric characters, dashes and underscores
    alpha_num // entirely alpha-numeric characters
    string // string, assign the nullable rule to allow the field to also be null
    digits:value // numeric and must have an exact length of value
    digits_between:min,max // length between the given min and max
    integer // integer
    numeric // numeric
    email // formatted as an e-mail address
    json // valid JSON string
    array // PHP array
    file // successfully uploaded file.
    image // file under validation must be an image (jpeg, png, bmp, gif, or svg)
    date // valid, non-relative date according to the strtotime PHP function
    date_equals:date // equal to the given date
    date_format:format // match the given format
    // use either date or date_format when validating a field, not both
    // dates will be passed into the PHP strtotime function
    timezone // valid timezone identifier according to the timezone_identifiers_list PHP function
    url // valid URL
    active_url // valid A or AAAA record according to the dns_get_record PHP function
    uuid // valid RFC 4122 (version 1, 3, 4, or 5) universally unique identifier (UUID)
    ip // IP address
    ipv4 // IPv4 address
    ipv6 // IPv6 address

    in_array:anotherfield.* // exist in anotherfield values
    min:value // minimum value
    max:value // less than or equal to a maximum value
    gt:field // greater than the given field
    gte:field // greater than or equal to the given field
    lt:field // less than the given field
    lte:field // less than or equal to the given field
    // two fields must be of the same type
    // strings, numerics, arrays, and files are evaluated using the same conventions as the size rule
    // the name of another field under validation may be supplied as the value of date

    between:min,max // size between the given min and max
    // strings, numerics, arrays, and files are evaluated in the same fashion as the size rule

    size:value // size matching the given value
    // or string data, value corresponds to the number of characters
    // for numeric data, value corresponds to a given integer value
    // for an array, size corresponds to the count of the array
    // for files, size corresponds to the file size in kilobytes

    present // present in the input data but can be empty
    filled // not be empty when it is present
    same:field // given field must match the field under validation
    different:field // different value than field
    nullable // may be null, validating primitive such as strings and integers that can contain null values
    accepted // yes, on, 1, or true, useful for validating "Terms of Service" acceptance
    confirmed // matching field of foo_confirmation
    // if is 'password', a matching 'password_confirmation' field must be present in the input

    current_password // field under validation must match the authenticated users password
    // specify an authentication guard using the rule's first parameter:
    // 'password' => 'current_password:api'

    required // present in the input data and not empty
    // field is considered "empty" if one of the following conditions are true:
    //   value is null
    //   value is an empty string
    //   value is an empty array or empty Countable object
    //   value is an uploaded file with no path

    distinct // when working with arrays, wihout duplicate values
    'foo.*.id' => 'distinct'

    starts_with:foo,bar,... // start with one of the given values
    ends_with:foo,bar,... // end with one of the given values

    in:foo,bar,... // included in the given list of values
      // ...
      use Illuminate\Validation\Rule;
      // ...
      Validator::make($data, [
        'zones' => [
          'required',
          Rule::in(['first-zone', 'second-zone']),
        ],
      ]);
      // ...

    not_in:foo,bar,... // not be included in the given list of values
    // Rule::notIn method may be used to fluently construct the rule:
      // ...
      use Illuminate\Validation\Rule;
      // ...
      Validator::make($data, [
        'toppings' => [
          'required',
          Rule::notIn(['sprinkles', 'cherries']),
        ],
      ]);
      // ...

    after:date // value after a given date
    before:date // value preceding the given date
    // name of another field under validation may be supplied as the value of date
    'start_date' => 'required|date|after:tomorrow'
    // instead of passing a date string to be evaluated by strtotime,
    // specify another field to compare against the date:
    'finish_date' => 'required|date|after:start_date'
    after_or_equal:date // value after or equal to the given date
    before_or_equal:date // value preceding or equal to the given date

    not_regex:pattern // not match the given regular expression
    regex:pattern // match the given regular expression
    // uses the PHP preg_match function
    // pattern specified should obey the same formatting required by preg_match
    // and thus also include valid delimiters: 'email' => 'not_regex:/^.+$/i'
    // when using the regex / not_regex patterns, specify rules in an array
    // instead of using pipe delimiters, especially if the regular expression contains a pipe character

    // present and not empty ...
    required_if:anotherfield,value,...
    // if the anotherfield field is equal to any value
    // Rule::requiredIf for more complex condition for the required_if rule
    // accepts a boolean or a Closure (should return true or false to indicate if is required)
      // ...
      use Illuminate\Validation\Rule;
      // ...
      Validator::make($request->all(), [
        'role_id' => Rule::requiredIf($request->user()->is_admin),
      ]);
      Validator::make($request->all(), [
          'role_id' => Rule::requiredIf(function () use ($request) {
              return $request->user()->is_admin;
          }),
      ]);
      // ...
    required_unless:anotherfield,value,... // unless the anotherfield field is equal to any value
    required_with:foo,bar,... // only if any of the other specified fields are present
    required_with_all:foo,bar,... // only if all of the other specified fields are present
    required_without:foo,bar,... // only when any of the other specified fields are not present
    required_without_all:foo,bar,... // only when all of the other specified fields are not present

    bail // stop running validation rules after the first validation failure

    ////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////

    dimensions // file under validation must be an image
    // meeting the dimension constraints as specified by the rule's parameters:
    'avatar' => 'dimensions:min_width=100,min_height=200'
    // available constraints: min_width, max_width, min_height, max_height, width, height, ratio
    // ratio constraint should be represented as width divided by height
    // can be specified either by a statement like 3/2 or a float like 1.5:
    'avatar' => 'dimensions:ratio=3/2'
    // since this rule requires several arguments,
    // use the Rule::dimensions method to fluently construct the rule:
      // ...
      use Illuminate\Validation\Rule;
      // ...
      Validator::make($data, [
        'avatar' => [
          'required',
          Rule::dimensions()->maxWidth(1000)->maxHeight(500)->ratio(3 / 2),
        ],
      ]);
      // ...

    mimetypes:text/plain,... // file under validation must match one of the given MIME types:
    'video' => 'mimetypes:video/avi,video/mpeg,video/quicktime'
    // to determine the MIME type of the uploaded file, file contents will be read
    // and the framework will attempt to guess the MIME type,
    // which may be different from the client provided MIME type.
    mimes:foo,bar,... // file under validation must have a one of the listed MIME type
    'photo' => 'mimes:jpeg,bmp,png'
    // https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types

    ////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////

    exists:table,column // exist on a given database table.
    'state' => 'exists:states' // if the column option is not specified, the field name will be used
    'state' => 'exists:states,abbreviation'
    // specify a specific database connection to be used for the exists query
    // prependin the connection name to the table name using "dot" syntax:
    'email' => 'exists:connection.staff,email'
    // customize the query executed by the validation rule,
    // specify the validation rules as an array instead of using the | character to delimit them:
      // ...
      use Illuminate\Validation\Rule;
      // ...
      Validator::make($data, [
        'email' => [
          'required',
          Rule::exists('staff')->where(function ($query) {
            $query->where('account_id', 1);
          }),
        ],
      ]);
      // ...

    unique:table,column,except,idColumn // not exist within the given database table.
    // --- custom column name, if not specified, the field name will be used
    'email' => 'unique:users,email_address'
    // --- custom database connection, connection and the table name using "dot" syntax:
    'email' => 'unique:connection.users,email_address'
    // --- forcing a unique rule to ignore a given id
    // use the Rule class to fluently define the rule
    // specify the validation rules as an array instead of using the | character to delimit the rules:
      // ...
      use Illuminate\Validation\Rule;
      // ...
      Validator::make($data, [
        'email' => [
          'required',
          Rule::unique('users')->ignore($user->id),
        ],
      ]);
      // ...
    // never pass any user controlled request input into the ignore method
    // only pass a system generated unique ID such as an auto-incrementing ID or UUID
    // instead of passing the model key value to the ignore method,
    // pass the entire model instance, Laravel will automatically extract the key from the model
    Rule::unique('users')->ignore($user)
    // --- specify the name of the column when calling the ignore method
    // if table uses a primary key column name other than id
    Rule::unique('users')->ignore($user->id, 'user_id')
    // --- pass a different column name as the second argument to the unique method
    Rule::unique('users', 'email_address')->ignore($user->id),
    // --- adding additional where clauses
    // specify additional query constraints by customizing the query using the where method
    // verifies the account_id is 1
    'email' => Rule::unique('users')->where(function ($query) {
      return $query->where('account_id', 1);
    })
  
Custom Validation Rules

    // php artisan make:rule Uppercase
    // generate a new rule object, contains two methods:
    // --- passes - receives the attribute value and name,
    // and should return true or false depending on whether the attribute value is valid or not
    // --- message - should return the validation error message
    // that should be used when validation fails
    namespace App\Rules;
    use Illuminate\Contracts\Validation\Rule;
    class Uppercase implements Rule {
      // determine if the validation rule passes
      public function passes($attribute, $value) {
        return strtoupper($value) === $value;
        // --- trans - helper, return an error message from translation files
        return trans('validation.uppercase');
      }
      // get the validation error message
      public function message() {
        return 'The :attribute must be uppercase.';
      }
    }
    // ...
    // ATTACH RULE TO VALIDATOR
    use App\Rules\Uppercase;
    // ...
    $request->validate([
      'name' => ['required', 'string', new Uppercase],
    ]);

    // --- Closure, instead of a rule object
    // receives the attribute name, value, and a $fail callback
    $validator = Validator::make($request->all(), [
      'title' => [
        'required',
        'max:255',
        function ($attribute, $value, $fail) {
          if ($value === 'foo') {
            $fail($attribute.' is invalid.');
          }
        },
      ],
    ]);

    // --- extend - use within a service provider to register a custom validation rule
    namespace App\Providers;
    use Illuminate\Support\ServiceProvider;
    use Illuminate\Support\Facades\Validator;
    class AppServiceProvider extends ServiceProvider {
      // register any application services
      public function register() {
        // ...
      }
      // bootstrap any application services
      public function boot() {
        // ...
        Validator::extend(
          'foo',
          function (
            $attribute,   // name of the $attribute being validated
            $value,       // value
            $parameters,  // array of parameters passed to the rule
            $validator    // Validator instance
          ) {
          return $value == 'foo';
        });
        // pass a class and method to the extend method instead of a Closure:
        Validator::extend('foo', 'FooValidator@validate');
        // custom placeholder replacements for error messages
        Validator::replacer(
          'foo',
          function (
            $message,
            $attribute,
            $rule,
            $parameters
          ) {
          return str_replace(...);
        });
        // for a rule to run even when an attribute is empty,
        // imply (assume) that the attribute is required
        // by default, when an attribute being validated is not present or contains an empty string,
        // normal validation rules, including custom extensions, are not run
        Validator::extendImplicit(
          'foo',
          function (
            $attribute,   // name of the $attribute being validated
            $value,       // value
            $parameters,  // array of parameters passed to the rule
            $validator    // Validator instance
          ) {
          return $value == 'foo';
        });
        // ...
      }
    }

    // error message for custom rule is set using an inline custom message array
    // or by adding an entry in the validation language file
    // message should be placed in the first level of the array, not within the custom array,
    // which is only for attribute-specific error messages:
    "foo" => "Your input was invalid!",
    "accepted" => "The :attribute must be accepted.",...
  

Views


    // --- view - returns views
    // resources/views/greeting.blade.php in routes definition
    Route::get('/', function () {
      return view('greeting', ['name' => 'James']);
    });

    // --- "dot" notation - reference nested views
    // resources/views/admin/profile.blade.php
    return view('admin.profile', $data);

    // --- exists - determine if a view exists
    use Illuminate\Support\Facades\View;
    // ...
    if (View::exists('emails.customer')) {
      // ...
    }

    // --- first - create the first view that exists in a given array of views
    return view()->first(['custom.admin', 'admin'], $data);
    // via the View facade
    use Illuminate\Support\Facades\View;
    // ...
    return View::first(['custom.admin', 'admin'], $data);

    // --- PASSING DATA TO VIEWS

    return view('greetings', ['name' => 'Victoria']);

    // --- with - add individual pieces of data to the view
    return view('greeting')->with('name', 'Victoria');

    // --- share - share a piece of data with all views that are rendered by application
    // place within a service provider boot method.
    // add them to the AppServiceProvider or generate a separate service provider to house them
    namespace App\Providers;
    use Illuminate\Support\Facades\View;
    class AppServiceProvider extends ServiceProvider {
      // register any application services
      public function register() {
        // ...
      }
      // bootstrap any application services
      public function boot() {
        View::share('key', 'value');
      }
    }

    // --- VIEW COMPOSERS
    // callbacks or class methods that are called when a view is rendered
    // for data that you want to be bound to a view each time that view is rendered,
    // organize that logic into a single location

    // register the view composers within a service provider
    // use the View facade to access the underlying Illuminate\Contracts\View\Factory contract
    // Laravel does not include a default directory for view composers
    // organize them however you wish, create an app/Http/View/Composers directory
    namespace App\Providers;
    use Illuminate\Support\Facades\View;
    use Illuminate\Support\ServiceProvider;
    class ViewServiceProvider extends ServiceProvider {
      // register any application services
      public function register() {
        // ...
      }
      // bootstrap any application services
      public function boot() {
        // using class based composers
        View::composer(
          'profile',
          // ['profile', 'dashboard'],  // attaching to multiple views
          // '*',                       // attach to all views
          'App\Http\View\Composers\ProfileComposer'
        );
        // using Closure based composers
        View::composer('dashboard', function ($view) {
          // ...
        });
        // creators - very similar to view composers
        // executed immediately after the view is instantiated
        // instead of waiting until the view is about to render
        View::creator(
          'profile',
          'App\Http\View\Creators\ProfileCreator'
        );
      }
    }
    // now that the composer is registered
    // ProfileComposer@compose method will be executed each time the profile view is being rendered
    // define the composer class
    namespace App\Http\View\Composers;
    use Illuminate\View\View;
    use App\Repositories\UserRepository;
    class ProfileComposer {
      // user repository implementation
      protected $users;
      // create a new profile composer
      public function __construct(UserRepository $users) {
        // dependencies automatically resolved by service container...
        $this->users = $users;
      }
      // bind data to the view
      // called before the view is rendered with the Illuminate\View\View instance
      // use the with method to bind data to the view
      public function compose(View $view) {
        $view->with('count', $this->users->count());
      }
    }
  

URL Generation


    // --- url
    // generate arbitrary URLs, uses scheme (HTTP or HTTPS) and host from the current request

    $post = App\Post::find(1);
    echo url("/posts/{$post->id}"); // http://example.com/posts/1

    echo url()->current(); // current URL without the query string
    echo url()->full(); // current URL including the query string
    echo url()->previous(); // full URL for the previous request

    // each method may also be accessed via the URL facade
    use Illuminate\Support\Facades\URL;
    // ...
    echo URL::current();

    // --- route - generate URLs to named routes

    // named routes allow generation of URLs without being coupled to the actual URL defined on the route
    // if the route URL changes, no changes need to be made to your route function calls
    Route::get('/post/{post}', function () {
      // ...
    })->name('post.show');
    // ...
    echo route('post.show', ['post' => 1]); // http://example.com/post/1

    // automatically extracts models primary key
    echo route('post.show', ['post' => $post]);

    // generate URLs for routes with multiple parameters
    Route::get('/post/{post}/comment/{comment}', function () {
      // ...
    })->name('comment.show');
    // ...
    echo route(
      'comment.show',
      ['post' => 1, 'comment' => 3]
    ); // http://example.com/post/1/comment/3

    // --- action - generate URL for the given controller action

    // pass the controller class name relative to the App\Http\Controllers namespace
    // no need to pass the full namespace of the controller
    $url = action('HomeController@index');
    // reference actions with a "callable" array syntax
    use App\Http\Controllers\HomeController;
    // ...
    $url = action([HomeController::class, 'index']);
    // if the controller method accepts route parameters,
    // pass them as the second argument to the function
    $url = action('UserController@profile', ['id' => 1]);

    // --- {locale} parameter - request-wide default values for certain URL parameters

    Route::get('/{locale}/posts', function () {
      // ...
    })->name('post.index');
    // URL::defaults
    // default value for parameter that will always be applied during the current request
    // may be called from a route middleware so that you have access to the current request
    namespace App\Http\Middleware;
    use Closure;
    use Illuminate\Support\Facades\URL;
    class SetDefaultLocaleForUrls {
      public function handle($request, Closure $next) {
        URL::defaults(['locale' => $request->user()->locale]);
        return $next($request);
      }
    }
    // once the default value for the locale parameter has been set,
    // you are no longer required to pass its value when generating URLs via the route helper

    // --- signedRoute - create "signed" URLs to named routes

    // URLs will have a "signature" hash appended to the query string
    // which allows Laravel to verify that the URL has not been modified since it was created
    // useful for routes that are publicly accessible, need a layer of protection
    use Illuminate\Support\Facades\URL;
    // ...
    return URL::signedRoute('unsubscribe', ['user' => 1]);
    // --- temporarySignedRoute - temporary signed route URL that expires
    use Illuminate\Support\Facades\URL;
    // ...
    return URL::temporarySignedRoute(
      'unsubscribe', now()->addMinutes(30), ['user' => 1]
    );
    // --- hasValidSignature - verify that an incoming request has a valid signature
    use Illuminate\Http\Request;
    // ...
    Route::get('/unsubscribe/{user}', function (Request $request) {
      if (! $request->hasValidSignature()) {
        abort(401);
      }
      // ...
    })->name('unsubscribe');
    // alternatively, assign Illuminate\Routing\Middleware\ValidateSignature middleware to route
    // if it is not already present, assign this middleware a key in HTTP kernel routeMiddleware array
    // application route middleware:
    protected $routeMiddleware = [
      'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
    ];
    // after middleware registration in kernel, attach it to a route
    // if the incoming request does not have a valid signature,
    // middleware will automatically return a 403 error response
    Route::post('/unsubscribe/{user}', function (Request $request) {
      // ...
    })->name('unsubscribe')->middleware('signed');
  

Performance

TOOLS

PHP 5.6 - 8.2


    // --- symmetric array destructuring, alternative to existing list()
    $data = [ [1, 'Tom'], [2, 'Fred'], ];
    list($id1, $name1) = $data[0]; // list() style
    [$id1, $name1] = $data[0]; // [] style
    foreach ($data as list($id, $name)) { // list() style
      // logic here with $id and $name
    }
    foreach ($data as [$id, $name]) { // [] style
      // logic here with $id and $name
    }
    // - reference assignments, also supported for list()
    [&$a, [$b, &$c]] = $d;
    // - keys in list()
    $data = [ ["id" => 1, "name" => 'Tom'], ["id" => 2, "name" => 'Fred'], ];
    list("id" => $id1, "name" => $name1) = $data[0]; // list() style
    ["id" => $id1, "name" => $name1] = $data[0]; // [] style
    foreach ($data as list("id" => $id, "name" => $name)) { // list() style
      // logic here with $id and $name
    }
    foreach ($data as ["id" => $id, "name" => $name]) { // [] style
      // logic here with $id and $name
    }

    // --- null coalescing - use a ternary in conjunction with isset()
    $username = $_GET['user'] ?? 'nobody'; // if $_GET['user'] does not exist returns 'nobody'
    // equivalent to:
    $username = isset($_GET['user']) ? $_GET['user'] : 'nobody';
    // can be chained, return first defined value
    // out of $_GET['user'], $_POST['user'], and 'nobody'.
    $username = $_GET['user'] ?? $_POST['user'] ?? 'nobody';
    // enclose them in parenthesis null coalesce on typecasted properties
    $foo = new StdClass;
    $foo->bas ?? 23; // >>> 23
    $foo = new StdClass;
    $bar = (int) ($foo->bas ?? 23);
    var_dump($bar); // >>> 23
    $foo = new StdClass;
    $bar = (int) $foo->bas ?? 23;
    var_dump($bar); // >>> Notice: Undefined property: stdClass::$bas
    // - null coalescing assignment operator
    $array['key'] ??= computeDefault();
    // roughly equivalent to
    if (!isset($array['key'])) { $array['key'] = computeDefault(); }

    // --- spaceship operator - comparing two expressions
    // returns -1, 0 or 1 when $a is respectively less than, equal to, or greater than $b
    // integers
    echo 1 <=> 1; // 0
    echo 1 <=> 2; // -1
    echo 2 <=> 1; // 1
    // floats
    echo 1.5 <=> 1.5; // 0
    echo 1.5 <=> 2.5; // -1
    echo 2.5 <=> 1.5; // 1
    // strings
    echo "a" <=> "a"; // 0
    echo "a" <=> "b"; // -1
    echo "b" <=> "a"; // 1
    echo 5 <=> 8; // 5 - 8 = -3 >>> -1
    echo 2 <=> 2; // 2 - 2 = 0  >>>  0
    echo 4 <=> 2; // 4 - 2 = 2  >>>  1

    // --- nullsafe methods and properties
    // if the object being dereferenced is null then null will be returned rather than an exception thrown.
    // if the dereference is part of a chain, the rest of the chain is skipped.
    $result = $repository?->getUser(5)?->name;
    // is equivalent to the following code block:
    if (is_null($repository)) {
        $result = null;
    } else {
        $user = $repository->getUser(5);
        if (is_null($user)) {
            $result = null;
        } else {
            $result = $user->name;
        }
    }

    // --- array constants can be defined with define()
    // in PHP 5.6, they could only be defined with const
    define('ANIMALS', [
      'dog',
      'cat',
      'bird'
    ]);
    echo ANIMALS[1]; // outputs "cat"

    // --- WeakMap
    // map (or dictionary) that accepts objects as keys.
    // an object in a key of WeakMap does not contribute toward the object's reference count.
    // if at any point the only remaining reference to an object is the key of a WeakMap,
    // the object will be garbage collected and removed from the WeakMap.
    // primary use case is for building caches of data derived from an object that do not need to live longer than the object
    final class WeakMap implements ArrayAccess, Countable, IteratorAggregate {
      /* Methods */
      public __construct()
      public count(): int // counts the number of live entries in the map
      public getIterator(): Iterator // retrieve an external iterator
      public offsetExists(object $object): bool // hecks whether a certain object is in the map
      public offsetGet(object $object): mixed // checks whether a certain object is in the map
      public offsetSet(object $object, mixed $value): void // updates the map with a new key-value pair
      public offsetUnset(object $object): void // removes an entry from the map
    }
    $wm = new WeakMap();
    $o = new StdClass;
    class A {
      public function __destruct() {
        echo "Dead!\n";
      }
    }
    $wm[$o] = new A;
    var_dump(count($wm));   // int(1)
    echo "Unsetting...\n";  // Unsetting...
    unset($o);              // Dead!
    echo "Done\n";          // Done
    var_dump(count($wm));   // int(0)

    // --- none capturing "catch" blocks
    try {
        $response = $this->sendRequest();
    } catch (RequestException $exception) { // before PHP 8.0
        Log::error('API request failed to send.');
    }
    try {
        $response = $this->sendRequest();
    } catch (RequestException) { // PHP 8.0
        Log::error('API request failed to send.');
    }
  
functions

    // --- arrow functions, same features as anonymous functions
    fn(array $x) => $x;
    static fn(): int => $x;
    fn($x = 42) => $x;
    fn(&$x) => $x;
    fn&($x) => $x;
    fn($x, ...$rest) => $rest;
    // variable used in the expression defined in parent scope will be implicitly captured by-value
    $y = 1;
    $fn1 = fn($x) => $x + $y;
    // equivalent to using $y by value:
    $fn2 = function ($x) use ($y) { return $x + $y; };
    var_export($fn1(3)); // >>> 4
    // values from the outer scope cannot be modified by arrow functions
    $x = 1;
    $fn = fn() => $x++; // no effect
    $fn();
    var_export($x);  // >>> 1
    // nested
    $z = 1;
    $fn = fn($x) => fn($y) => $x * $y + $z;
    var_export($fn(5)(10)); // >>> 51

    // --- chain functions
    function add($a) {
      return function($b) use ($a) {
        return $a + $b;
      };
    }
    // PHP 7
    add(10)(15); // >>> int 25
    // PHP 5.6
    $add10 = add(10);
    $add10(15); // >>> int 25

    // --- variadic functions
    function f($req, $opt = null, ...$params) {
      // $params is an array containing the remaining arguments.
      printf('$req: %d; $opt: %d; number of params: %d'."\n",
            $req, $opt, count($params));
    }
    // f(1, 2, 3, 4, 5) >>> $req: 1; $opt: 2; number of params: 3
    class B extends A {
      public function method(...$everything) {}
    }

    // --- array_is_list(array $array) - determines if the given array is a list
    // if its keys consist of consecutive numbers from 0 to count($array)-1
    array_is_list([]); // true
    array_is_list(['apple', 2, 3]); // true
    array_is_list([0 => 'apple', 'orange']); // true
    array_is_list([1 => 'apple', 'orange']); // false - does not start at 0
    array_is_list([1 => 'apple', 0 => 'orange']); // false - keys are not in the correct order
    array_is_list([0 => 'apple', 'foo' => 'bar']); // false - non-integer keys
    array_is_list([0 => 'apple', 2 => 'bar']); // false - non-consecutive keys

    // --- array unpacking
    // using short array syntax, also, works with array() syntax
    $arr1 = [1, 2, 3];
    $arr2 = [...$arr1]; //[1, 2, 3]
    $arr3 = [0, ...$arr1]; //[0, 1, 2, 3]
    $arr4 = [...$arr1, ...$arr2, 111]; // [1, 2, 3, 1, 2, 3, 111]
    $arr5 = [...$arr1, ...$arr1]; // [1, 2, 3, 1, 2, 3]
    function getArr() {
      return ['a', 'b'];
    }
    $arr6 = [...getArr(), 'c' => 'd']; // ['a', 'b', 'c' => 'd']
    // array unpacking with duplicate key
    // string key
    $arr1 = ["a" => 1];
    $arr2 = ["a" => 2];
    $arr3 = ["a" => 0, ...$arr1, ...$arr2];
    var_dump($arr3); // ["a" => 2]
    // integer key
    $arr4 = [1, 2, 3];
    $arr5 = [4, 5, 6];
    $arr6 = [...$arr4, ...$arr5];
    var_dump($arr6); // [1, 2, 3, 4, 5, 6] , [0 => 1, 1 => 2, 2 => 3, 3 => 4, 4 => 5, 5 => 6]
    // prior to PHP 8.1, unpacking an array which has a string key is not supported:
    $arr1 = [1, 2, 3];
    $arr2 = ['a' => 4];
    $arr3 = [...$arr1, ...$arr2];

    // --- argument unpacking
    function add($a, $b, $c) {
      return $a + $b + $c;
    }
    // $operators = [2, 3];
    // add(1, ...$operators) >>> 6
    // unpacking inside arrays:
    $parts = ['apple', 'pear'];
    $fruits = ['banana', ...$parts, 'watermelon']; // >>> ['banana', 'apple', 'pear', 'watermelon'];
    array_merge(...$arrays)
    // specifying named arguments after an argument unpack
    foo(...$args, named: $arg)

    // --- named arguments
    // passing arguments to a function based on the parameter name, rather than the parameter position
    // self-documenting arguments, order-independent, allows skipping default values arbitrarily
    // parameter name must be an identifier, specifying dynamically is not allowed
    myFunction(paramName: $value);
    array_foobar(array: $value);
    // using positional arguments:
    array_fill(0, 100, 50);
    // using named arguments:
    array_fill(start_index: 0, count: 100, value: 50);
    // same but with different order of parameters
    array_fill(value: 50, count: 100, start_index: 0);
    // combining named arguments with positional arguments:
    htmlspecialchars($string, double_encode: false);
    // same as:
    htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'UTF-8', false);
    // use named arguments after unpacking:
    function foo($a, $b, $c = 3, $d = 4) {
      return $a + $b + $c + $d;
    }
    var_dump(foo(...[1, 2], d: 40)); // 46
    var_dump(foo(...['b' => 2, 'a' => 1], d: 40)); // 46
    // error thrown when passing the same parameter multiple times:
    function foo($param) { ... }
    foo(param: 1, param: 2); // Error: Named parameter $param overwrites previous argument
    foo(1, param: 2); // Error: Named parameter $param overwrites previous argument
    class ProcessImage {
      public static function handle(string $path, int $height, int $width, string $type, int $quality, int $compression): void {
          // logic for handling image processing
      }
    }
    ProcessImage::handle('/path/to/image.jpg', 500, 300, 'jpg', 100, 5); // Before PHP 8.0
    ProcessImage::handle( // PHP 8.0
      path: '/path/to/image.jpg',
      height: 500,
      width: 300,
      type: 'jpg',
      quality: 100,
      compression: 5,
    );

    // --- first class callable syntax
    // a way of creating anonymous functions from callable
    // supersedes existing callable syntax using strings and arrays
    // it is accessible to static analysis, and uses the scope at the point where the callable is acquired
    class Foo {
       public function method() {}
       public static function staticmethod() {}
       public function __invoke() {}
    }
    $obj = new Foo();
    $classStr = 'Foo';
    $methodStr = 'method';
    $staticmethodStr = 'staticmethod';

    $f1 = strlen(...);
    $f2 = $obj(...);  // invokable object
    $f3 = $obj->method(...);
    $f4 = $obj->$methodStr(...);
    $f5 = Foo::staticmethod(...);
    $f6 = $classStr::$staticmethodStr(...);
    // traditional callable using string, array
    $f7 = 'strlen'(...);
    $f8 = [$obj, 'method'](...);
    $f9 = [Foo::class, 'staticmethod'](...);
    // ... is part of the syntax, and not an omission
    // CallableExpr(...) has the same semantics as Closure::fromCallable()
    // unlike callable using strings and arrays, CallableExpr(...) respects the scope at the point where it is created
    // CallableExpr(...) and traditional callable:
    class Foo {
      public function getPrivateMethod() {
        return [$this, 'privateMethod'];
      }
      private function privateMethod() {
        echo __METHOD__, "\n";
      }
    }
    $foo = new Foo;
    $privateMethod = $foo->getPrivateMethod();
    $privateMethod(); // Fatal error: Call to private method Foo::privateMethod() from global scope
    // because call is performed outside from Foo and visibility will be checked from this point
    class Foo1 {
        public function getPrivateMethod() {
            // Uses the scope where the callable is acquired.
            return $this->privateMethod(...); // identical to Closure::fromCallable([$this, 'privateMethod']);
        }
        private function privateMethod() {
            echo __METHOD__, "\n";
        }
    }
    $foo1 = new Foo1;
    $privateMethod = $foo1->getPrivateMethod();
    $privateMethod();  // Foo1::privateMethod
    // cannot be combined with the nullsafe operator, compile-time error:
    $obj?->method(...);
    $obj?->prop->method(...);
  
built-in functions

    // --- str_contains(string $haystack, string $needle), str_starts_with(), str_ends_with()
    // case-sensitive check

    // --- intdiv() - integer division of its operands
    intdiv(10, 3)) // >>> int(3)
    // --- fdiv() - floating-point division under IEEE 754 semantics
    // division by zero is considered well-defined and will return one of Inf, -Inf or NaN

    // --- hash_equals()
    $expected  = crypt('12345', '$2a$07$usesomesillystringforsalt$');
    $correct   = crypt('12345', '$2a$07$usesomesillystringforsalt$');
    $incorrect = crypt('1234',  '$2a$07$usesomesillystringforsalt$');
    var_dump(hash_equals($expected, $correct));   // >>> true
    var_dump(hash_equals($expected, $incorrect)); // >>> false

    // --- unserialize()
    // better security when unserializing objects on untrusted data
    // prevents possible code injections with a whitelist classes that can be unserialized
    // converts all objects into __PHP_Incomplete_Class object
    $data = unserialize($foo, ["allowed_classes" => false]);
    // converts all objects into __PHP_Incomplete_Class object except those of MyClass and MyClass2
    $data = unserialize($foo, ["allowed_classes" => ["MyClass", "MyClass2"]]);
    // default behaviour (same as omitting the second argument) that accepts all classes
    $data = unserialize($foo, ["allowed_classes" => true]);

    // --- preg_replace_callback_array($pattern, string|array $subject, int $limit = -1, int &$count = null, $flags = 0)
    // perform RegEx search and replace using callbacks
    $subject = 'Aaaaaa Bbb';
    preg_replace_callback_array([
      '~[a]+~i' => function ($match) {
        echo strlen($match[0]), ' matches for "a" found', PHP_EOL;
      },
      '~[b]+~i' => function ($match) {
        echo strlen($match[0]), ' matches for "b" found', PHP_EOL;
      }
    ], $subject);
    // >>> 6 matches for "a" found
    // >>> 3 matches for "b" found

    // --- match - branches evaluation based on an identity check of a value
    // similarly to a switch statement.
    // comparison is an identity check (===) rather than a weak equality check (==)
    // if the subject expression is not handled by ANY match arm an UnhandledMatchError is thrown
    $return_value = match (subject_expression) {
      single_conditional_expression => return_expression,
      conditional_expression1, conditional_expression2 => return_expression,
    };
    // basic match usage
    $food = 'cake';
    $return_value = match ($food) {
      'apple' => 'This food is an apple',
      'bar' => 'This food is a bar',
      'cake' => 'This food is a cake',
    }; // string(19) "This food is a cake"
    $result = match ($x) {
      foo() => ...,
      $this->bar() => ..., // $this->bar() isn't called if foo() === $x
      $this->baz => beep(), // beep() isn't called unless $x === $this->baz
      // etc.
      // this match arm:
      $a, $b, $c => 5,
      // is equivalent to these three match arms:
      $a => 5,
      $b => 5,
      $c => 5,
      // default pattern - matches anything that wasn't previously matched
      default => baz(),
    };
    // handle non identity checks - using true as the subject expression
    $age = 23;
    $result = match (true) {
        $age >= 65 => 'senior',
        $age >= 25 => 'adult',
        $age >= 18 => 'young adult',
        default => 'kid',
    }; // string(11) "young adult"
    $text = 'Bienvenue chez nous';
    $result = match (true) {
        str_contains($text, 'Welcome') || str_contains($text, 'Hello') => 'en',
        str_contains($text, 'Bienvenue') || str_contains($text, 'Bonjour') => 'fr',
        // ...
    }; // string(2) "fr"

    // --- Closure::call()
    // temporarily binding an object scope to a closure and invoking it
    class A {private $x = 1;}
    // Pre PHP 7 code
    $getX = function() { return $this->x; };
    $getXCB = $getX->bindTo(new A, 'A'); // intermediate closure
    echo $getXCB(); // >>> 1
    // PHP 7+ code
    $getX = function() { return $this->x; };
    echo $getX->call(new A); // >>> 1

    // --- fsync(resource $stream) - synchronize changes to the file, including its meta-data
    // similar to fflush(), but it also instructs the operating system to write to the storage media
    // stream - must point to a file successfully opened by fopen() or fsockopen() (and not yet closed by fclose())
    $file = 'test.txt';
    $stream = fopen($file, 'w');
    fwrite($stream, 'test data');
    fwrite($stream, "\r\n");
    fwrite($stream, 'additional data');
    fsync($stream);
    fclose($stream);
    // --- fdatasync(resource $stream) — synchronizes data (but not meta-data) to the file

    // --- memory_reset_peak_usage() - resets the peak memory usage returned by the memory_get_peak_usage() function
    var_dump(memory_get_peak_usage()); // int(422440)
    $a = str_repeat("Hello", 424242);
    var_dump(memory_get_peak_usage()); // int(2508672)
    unset($a);
    memory_reset_peak_usage();
    $a = str_repeat("Hello", 2424);
    var_dump(memory_get_peak_usage()); // int(399208)

    // --- "random" extension,  always included when compiling PHP
    // all of the functions continue to reside in the global namespace:
    // functions and constants are now moved to the random extension
    // rand, getrandmax, srand, lcg_value,  mt_rand, mt_getrandmax, mt_srand
    // MT_RAND_PHP, MT_RAND_MT19937
    // random_int($min, $max) - cryptographic random integers
    var_dump(random_int(100, 999)); // >>> int(248)
    var_dump(random_int(-1000, 0)); // >>> int(-898)
    // random_bytes($length) - cryptographically secure pseudo-random bytes
    $bytes = random_bytes(5);
    bin2hex($bytes) // >>> string(10) "385e33f741"
    // Random\Randomizer class- object-oriented API to access all Random Number generation functionality
    // with a choice of Pseudo Random Number Generator algorithm, which can be swapped out with a new implementation
    $r = new Random\Randomizer();
    echo $r->getInt(1, 100); // 42
    echo $r->shuffleBytes('lorem ipsum'); // "ols mpeurim"
    // random-number between 1 and 100
    $randomizer = new Random\Randomizer();
    $randomizer->getInt(1, 100); // 42
    // shuffle a string
    $randomizer = new Random\Randomizer();
    $randomizer->shuffleBytes('abcdef'); // baecfd - does exactly what it says on the name, for multi-byte characters produces distorted/Mojibake text
    // shuffle an array
    $randomizer = new Random\Randomizer();
    $randomizer->shuffleArray(['apple', 'banana', 'orange']); // ['orange', 'apple', 'banana']
    // Mt19937 Engine
    $randomizer = new Random\Randomizer(new Random\Engine\Mt19937());
    $randomizer->getInt(1, 100); // 68
    // Mt19937 Engine with seed
    $randomizer = new Random\Randomizer(new Random\Engine\Mt19937(42));
    $randomizer->getInt(1, 100); // 43
    // Xoshiro256StarStar Engine with seed
    $randomizer = new Random\Randomizer(new Random\Engine\Xoshiro256StarStar(hash("sha256", "some seed value")));
    $randomizer->getInt(1, 100); // 43
    // PcgOneseq128XslRr64 Engine with seed
    $randomizer = new Random\Randomizer(new Random\Engine\PcgOneseq128XslRr64(hash("md5", "some seed value")));
    $randomizer->getInt(1, 100); // 43
    // using a mock Engine that returns the same value, to be used in unit tests
    class XKCDRandomEngine implements \Random\Engine {
      public function generate(): string {
          return \pack('V', 4); // Chosen by fair dice roll, guaranteed to be random
      }
    }
    $randomizer = new Random\Randomizer(new XKCDRandomEngine());
    $randomizer->getInt(0, 100); // 4
    // replacing random_bytes calls
    $randomValue = random_bytes(32); // Retrieves 32 random bytes.
    $randomizer = new Random\Randomizer();
    $randomizer->getBytes(32);
  
type declarations

    // class/interface"
    // name|self|parent|callable|array|bool|float|int|string|iterable|object|mixed|null|false|true
    // never - function either exit(), throws an exception, or doesn't terminate

    // - scalar type - wrong type is coerced into expected !
    function sum($a, $b): float { return $a + $b; }
    sum(1, 2); // >>> float(3)
    // - strict typing for arguments values, only defined for scalar type declarations
    declare(strict_types=1);

    // - return type, return only types: void|static
    function arraysSum(array ...$arrays):array {
    return array_map(function(array $array): int {
        return array_sum($array);
      }, $arrays);
    }
    class C {}
    class D extends C {}
    class E {} // doesnt extend C
    function f(C $c) { echo $c::class."\n"; }
    f(new C); // C
    f(new D); // D
    f(new E); // >>> Uncaught TypeError
    // - void - empty or no return
    function swap(&$left, &$right): void {
      if ($left === $right) { return; }
      $tmp = $left;
      $left = $right;
      $right = $tmp;
      // [ $left, $right ] = [ $right, $left ];
    }
    $a = 1;
    $b = 2;
    var_dump(swap($a, $b), $a, $b); // >>> null >>> int(2) >>> int(1)
    // - nullable - specified type or null
    function f(?C $c) { var_dump($c); }
    f(new C); // >>> object(C)#1 (0) { }
    f(null);  // >>> NULL
    print_r(arraysSum([1,2,3], [4,5,6], [7,8,9])); // >>> [6,15,24]
    // specified type or null can be passed as an argument or returned as a value, respectively
    function testReturn(): ?string { return 'elePHPant'; }
    var_dump(testReturn()); // >>> string(10) "elePHPant"
    function testReturn(): ?string { return null; }
    var_dump(testReturn()); // >>> NULL
    function test(?string $name) { var_dump($name); }
    test('elePHPant'); // >>> string(10) "elePHPant"
    test(null); // >>> NULL
    test(); // >>> Uncaught Error: Too few arguments to function test()

    // - union - T1|T2|... , nullable union: T1|T2|null
    // allows any of the declared types
    class PostService {
      public function all(): mixed { // before PHP 8.0
        if (! Auth::check()) { return []; }
        return Post::query()->get();
      }
    }
    class PostService {
      public function all(): array|Collection { // PHP 8.0
        if (! Auth::check()) { return []; }
        return Post::query()->get();
      }
    }

    class GFG {
      private int|float $CodingScore;
      public function setScore(int|float $CodingScore): void {
          $this->CodingScore = $CodingScore;
      }
      public function getScore(): int|float {
          return $this->CodingScore;
      }
    }
    $a = new GFG();
    $a->setScore(120.8);
    echo  $a->getScore(),"\r\n"; // 120.8
    $a->setScore(100);
    echo $a->getScore(); // 100

    // - mixed - accepts every value
    // equivalent to the union type object|resource|array|string|float|int|bool|null
    // the top type, every other type is a subtype of it

    // - intersection types
    // declaring a type for a parameter, property, or return types
    // and enforce that values belong to all of the declared class/interface types:
    function count_and_iterate(Iterator&\Countable $value) {
      foreach($value as $val) {}
      count($value);
    }
    // $value parameter must be an object from class that implements both Iterator and Countable interfaces
    // passing any other value causes a type error
    class CountableIterator implements \Iterator, \Countable {
        public function current(): mixed {}
        public function key(): mixed {}
        public function next(): void {}
        public function rewind(): void {}
        public function valid(): bool {}

        public function count(): int {}
    }
    count_and_iterate(new CountableIterator()); // OK
    count_and_iterate(new stdClass()); // Fatal error: ... Argument #1 ($value) must be of type Iterator&Countable, stdClass given
    // ! only support class and interface names as intersection members
    // scalar types, array, void, mixed, callable, never, iterable, null, static, parent, self, and other types are not allowed
    class A {
      public function test(Foo&Bar $val) {}
      public function test2(): Foo {}
    }
    class B extends A {
      public function test(Foo $val): Test&dsa {}
      public function test2(): Foo&Bar {}
    }

    // --- Disjunctive Normal Form (DNF) Types - combine union and intersection types,
    // strict rule: when combining union and intersection types, intersection types must be grouped with brackets
    class Foo {
      public function bar(mixed $entity) {
        if ((($entity instanceof A) && ($entity instanceof B)) || ($entity === null)) { // before
          return $entity;
        }
        throw new Exception('Invalid entity');
      }
      public function bar((A&B)|null $entity) { // PHP 8.2
        return $entity;
      }
    }
    (A&B)|D // accept an object that implements both A and B, OR an object that implements D
    C|(X&D)|null // object that implements C, OR a child of X that also implements D, OR null
    (A&B&D)|int|null // object that implements all three of A, B, and D, OR an int, OR null
    // order of types within each AND/OR section is irrelevant, following type declarations are all equivalent:
    (A&B)|(C&D)|(Y&D)|null
    (B&A)|null|(D&Y)|(C&D)
    null|(C&D)|(B&A)|(Y&D)
    // - return co-variance
    // when extending a class, method return type may narrow only,
    // it must be the same or more restrictive as its parent, additional ANDs may be added, but not additional ORs
    interface ITest {
      public function stuff(): (A&B)|D;
    }
    class TestOne implements ITest {
      public function stuff(): (A&B) {} // Acceptable.  A&B is more restrictive
    }
    class TestTwo implements ITest {
      public function stuff(): D {} // Acceptable. D is is a subset of A&B|D
    }
    class TestThree implements ITest {
      public function stuff(): C|D {} // // Acceptable, since C is a subset of A&B, even though it is not identical
    }
    // ! Not acceptable. This would allow an object that implements A but not B, which is wider than the interface
    class TestFour implements ITest {
      public function stuff(): A|D {}
    }
    interface ITestTwo {
      public function things(): C|D {}
    }
    // ! Not acceptable. Although C extends A and B, it's possible
    // for an object to implement A and B without implementing C.
    // Thus this definition is wider, and not allowed.
    class TestFive implements ITestTwo {
      public function things(): (A&B)|D {}
    }
    // - parameter contra-variance
    // when extending a class, a method parameter type may widen only
    // it must be the same or less restrictive as its parent, additional ORs may be added, but not additional ANDs
    interface ITest {
      public function stuff((A&B)|D $arg): void {}
    }
    class TestOne implements ITest {
      public function stuff((A&B)|D|Z $arg): void {} // Acceptable. Everything that ITest accepts is still valid and then some
    }
    class TestOne implements ITest {
      public function stuff(A|D $arg): void {} // Acceptable. This accepts objects that implement just A, which is a super-set of those that implement A&B
    }
    class TestOne implements ITest {
      public function stuff((A&B) $arg): void {} // !  Not acceptable. The interface says D is acceptable, but this class does not
    }
    interface ITestTwo {
      public function things(C|D $arg): void;
    }
    // Acceptable. Anything that implements C implements A&B,
    // but this rule also allows classes that implement A&B
    // directly, and thus is wider.
    class TestFive implements ITestTwo {
      public function things((A&B)|D $arg): void;
    }
  
classes, namespaces

    // --- properties type declarations
    class User {
      public int $id;
      public string $name;
    }

    // --- use function|const
    namespace Name\Space {
      const FOO = 42;
      function f() { echo __FUNCTION__."\n"; } // >>> 42
    }
    namespace {
      use const Name\Space\FOO;
      use function Name\Space\f;
      echo FOO."\n"; // >>> Name\Space\f
      f();
    }

    // --- class constant visibility
    class ConstDemo {
      const PUBLIC_CONST_A = 1;
      public const PUBLIC_CONST_B = 2;
      protected const PROTECTED_CONST = 3;
      private const PRIVATE_CONST = 4;
    }

    // --- final constants
    class Foo {
      final public const X = "foo";
    }
    class Bar extends Foo {
      public const X = "bar"; // // Fatal error: Bar::X cannot override final constant Foo::X
    }

    // --- readonly properties - prevents modification of the property after initialization
    // readonly static properties are not supported
    // readonly property can only be initialized once, and only from the scope where it has been declared
    // specifying an explicit default value on readonly properties is not allowed
    // cannot be unset() once they are initialized,
    // it is possible to unset a readonly property prior to initialization, from the scope where the property has been declared
    class Test {
      public readonly string $prop;
      public function __construct(string $prop) {
        $this->prop = $prop; // legal initialization
      }
    }
    $test = new Test("foobar");
    var_dump($test->prop); // string(6) "foobar" - legal read
    // illegal reassignment, does not matter that the assigned value is the same:
    $test->prop = "foobar"; // Error: Cannot modify readonly property Test::$prop
    // illegal initialization of readonly properties
    class Test1 {
      public readonly string $prop;
    }
    $test1 = new Test1; // Illegal initialization outside of private scope.
    $test1->prop = "foobar"; // Error: Cannot initialize readonly property Test1::$prop from global scope
    class Test {
        public readonly int $prop = 42; // Fatal error: Readonly property Test::$prop cannot have default value
    }
    // objects (or resources) stored in readonly properties may still be modified internally:
    class Test {
      public function __construct(public readonly object $obj) {}
    }
    $test = new Test(new stdClass);
    $test->obj->foo = 1; // legal interior mutation
    $test->obj = new stdClass; // illegal reassignment

    // --- group 'use' declarations
    // Pre PHP 7 code
    use some\namespace\ClassA;
    use some\namespace\ClassC as C;
    use function some\namespace\fn_a;
    use function some\namespace\fn_b;
    use const some\namespace\ConstA;
    use const some\namespace\ConstB;
    // PHP 7+ code
    use some\namespace\{ClassA, ClassB, ClassC as C};
    use function some\namespace\{fn_a, fn_b, fn_c};
    use const some\namespace\{ConstA, ConstB, ConstC};

    // --- anonymous classes has been added via new class
    // can be used in place of full class definitions for throwaway objects
    interface Logger {
      public function log(string $msg);
    }
    class Application {
      private $logger;
      public function getLogger(): Logger {
        return $this->logger;
      }
      public function setLogger(Logger $logger) {
        $this->logger = $logger;
      }
    }
    $app = new Application;
    $app->setLogger(new class implements Logger {
      public function log(string $msg) { echo $msg; }
    });
    var_dump($app->getLogger()); // >>> object(class@anonymous)#2 (0) { }

    // --- constructor promotion - set properties on objects directly in the constructor as an argument.
    // constructor parameters is assigned to a property in the constructor but otherwise not operated upon.
    // when a constructor argument includes a visibility modifier,
    // PHP will interpret it as both an object property and a constructor argument, and assign the argument value to the property.
    // constructor body may then be empty or may contain other statements.
    // any additional statements will be executed after the argument values have been assigned to the corresponding properties.
    // is possible to mix and match promoted and not-promoted arguments, in any order, no impact on code calling the constructor
    class Client {
      private string $url; // before PHP 8.0
      public function __construct(string $url) {
        $this->url = $url;
      }
    }
    class Client {
      public function __construct(
        private string $url, // PHP 8.0
      ) {}
    }
    class Point {
      public function __construct(protected int $x, protected int $y = 0) {
      }
    }

    // --- new() in initializers
    // objects can be used as default parameter values, static variables, and global constants, in attribute arguments
    // allowed:
    static $x = new Foo;
    const C = new Foo;
    function test($param = new Foo) {}
    #[AnAttribute(new Foo)]
    class Test {
      public function __construct(
        public $prop = new Foo,
      ) {}
    }
    class BuyerWorkflow {
      public function __construct(
          private WorkflowStepContract $step = new InitialBuyerStep(),
      ) {}
    }
    // not allowed (compile-time error):
    function test(
      $a = new (CLASS_NAME_CONSTANT)(), // dynamic class name
      $b = new class {}, // anonymous class
      $c = new A(...[]), // argument unpacking
      $d = new B($abc), // unsupported constant expression
    ) {}

    // --- creating an instance using an arbitrary expression
    class ClassA extends \stdClass {}
    class ClassB extends \stdClass {}
    class ClassC extends ClassB {}
    class ClassD extends ClassA {}
    function getSomeClass(): string {
        return 'ClassA';
    }
    var_dump(new (getSomeClass()));
    var_dump(new ('Class' . 'B'));
    var_dump(new ('Class' . 'C'));
    var_dump(new (ClassD::class));

    // --- private methods declared on a parent class
    // no longer enforce any inheritance rules on the methods of a child class
    // (with the exception of final private constructors)
    class ParentClass {
        private function method1() {}
        private function method2() {}
        private static function method3() {}
        // Throws a warning, as "final" no longer has an effect:
        private final function method4() {}
    }
    class ChildClass extends ParentClass {
        // All of the following are now allowed, even though the modifiers aren't
        // the same as for the private methods in the parent class.
        public abstract function method1() {}
        public static function method2() {}
        public function method3() {}
        public function method4() {}
    }

    // --- readonly classes (PHP 8.2.0)
    // add the readonly modifier to every declared property, and prevent the creation of dynamic properties
    // it is impossible to add support for them by using the AllowDynamicProperties attribute.
    #[AllowDynamicProperties]
    readonly class Foo { // Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo
    }
    ?>
    // neither untyped, nor static properties can be marked with the readonly modifier, readonly classes cannot declare them either:
    readonly class Foo {
        public $bar; // Fatal error: Readonly property Foo::$bar must have type
    }
    readonly class Foo {
        public static int $bar; // Fatal error: Readonly class Foo cannot declare static properties
    }
    // readonly class can be extended if, and only if, the child class is also a readonly class

    // --- constants in traits
    trait FooBar {
      const FOO = 'foo';
      private const BAR = 'bar';
      final const BAZ = 'baz';
      final protected const QUX = 'qux';
    }
    class Test {
      use FooBar;
    }
    echo Test::BAZ; // 'bar'

    // --- dynamic property deprecation
    class User {
      public $name;
    }
    $user = new User();
    $user->last_name = 'Doe'; // "Deprecated" notice !
    $user = new stdClass();
    $user->last_name = 'Doe'; // Still allowed

    #[AllowDynamicProperties] // allow dynamic properties using the AllowDynamicProperties attribute
    class User() {}
    $user = new User();
    $user->foo = 'bar';
  
generators

    // https://www.php.net/manual/en/language.generators.syntax.php
    // implement simple iterators
    // write code that uses foreach to iterate over a set of data without in memory array
    // instead of returning a value, yields as many values as it needs to
    // - current - get yielded value
    // - getReturn - get return value of a generator
      $gen = (function() { yield 1; yield 2; return 3; })();
      foreach ($gen as $val) { echo $val, PHP_EOL; }
      echo $gen->getReturn(), PHP_EOL;
      // >>> 1 >>> 2 >>> 3
    // - key - get yielded key
      function Gen(){ yield 'key' => 'value'; }
      $gen = Gen();
      echo "{$gen->key()} => {$gen->current()}";
      // >>> key => value
    // - next - resume execution of the generator
    // - rewind - rewind the iterator
    // - send - send a value to the generator and resumes execution
      function printer() {
        echo "I'm printer!".PHP_EOL;
        while (true) { $string = yield; echo $string.PHP_EOL; }
      }
      $printer = printer();
      $printer->send('Hello world!');
      $printer->send('Bye world!');
      // >>> I'm printer! >>> Hello world! >>> Bye world!
    // - throw - throw an exception into the generator and resumes execution
      function gen() {
        echo "Foo\n";
        try { yield; }
        catch (Exception $e) { echo "Exception: {$e->getMessage()}\n"; }
        echo "Bar\n";
      }
      $gen = gen();
      $gen->rewind();
      $gen->throw(new Exception('Test'));
      // >>> Foo >>> Exception: Test >>> Bar
    // - valid - check if the iterator has been closed
    // - __wakeup - serialize callback
    function xrange($start, $limit, $step = 1) {
      if ($start <= $limit) {
        if ($step <= 0) { throw new LogicException('Step must be positive'); }
        for ($i = $start; $i <= $limit; $i += $step) { yield $i; }
      } else {
        if ($step >= 0) { throw new LogicException('Step must be negative'); }
        for ($i = $start; $i >= $limit; $i += $step) { yield $i; }
      }
    }
    foreach (range(1, 9, 2) as $number) { echo "$number "; }
    foreach (xrange(1, 9, 2) as $number) { echo "$number "; }
    // range() and xrange() result is the same >>> 1 3 5 7 9
    function fib($n) {
      $cur = 1;
      $prev = 0;
      for ($i = 0; $i < $n; $i++) {
        yield $cur;
        $temp = $cur;
        $cur = $prev + $cur;
        $prev = $temp;
      }
    }
    $fibs = fib(9); // >>> 1 1 2 3 5 8 13 21 34

    // generator delegation
    function gen() { yield 1; yield 2; yield from gen2(); }
    function gen2() { yield 3; yield 4; }
    foreach (gen() as $val){ echo $val, PHP_EOL; } // >>> 1 >>> 2 >>> 3 >>> 4
  
attributes

    // add structured, machine-readable metadata information on declarations in code:
    // classes, methods, functions, parameters, properties and class constants can be the target of an attribute.
    // metadata defined by attributes can then be inspected at runtime using the Reflection APIs
    // attributes could therefore be thought of as a configuration language embedded directly into code.
    // generic implementation of a feature and its concrete use in an application can be decoupled..
    // comparable to interfaces and their implementations.
    // interfaces and implementations are about code,
    // attributes are about annotating extra information and configuration.
    // interfaces can be implemented by classes,
    // yet attributes can also be declared on methods, functions, parameters, properties and class constants.
    // more flexible than interfaces.
    // simple example of attribute usage is to convert an interface that has optional methods to use attributes.
    // ActionHandler interface representing an operation in an application,
    // where some implementations of an action handler require setup and others do not.
    // instead of requiring all classes that implement ActionHandler to implement a method setUp(), an attribute can be used.
    // benefit of this approach is that we can use the attribute several times:
    interface ActionHandler {
        public function execute();
    }

    #[Attribute]
    class SetUp {}

    class CopyFile implements ActionHandler {
        public string $fileName;
        public string $targetDirectory;

        #[SetUp]
        public function fileExists() {
            if (!file_exists($this->fileName)) {
                throw new RuntimeException("File does not exist");
            }
        }

        #[SetUp]
        public function targetDirectoryExists() {
            if (!file_exists($this->targetDirectory)) {
                mkdir($this->targetDirectory);
            } elseif (!is_dir($this->targetDirectory)) {
                throw new RuntimeException("Target directory $this->targetDirectory is not a directory");
            }
        }

        public function execute() {
            copy($this->fileName, $this->targetDirectory . '/' . basename($this->fileName));
        }
    }

    function executeAction(ActionHandler $actionHandler)    {
        $reflection = new ReflectionObject($actionHandler);
        foreach ($reflection->getMethods() as $method) {
            $attributes = $method->getAttributes(SetUp::class);
            if (count($attributes) > 0) {
                $methodName = $method->getName();
                $actionHandler->$methodName();
            }
        }
        $actionHandler->execute();
    }

    $copyAction = new CopyFile();
    $copyAction->fileName = "/tmp/foo.jpg";
    $copyAction->targetDirectory = "/home/user";
    executeAction($copyAction);

    // --- syntax
    // a.php
    namespace MyExample;
    use Attribute;
    #[Attribute]
    class MyAttribute {
      const VALUE = 'value';
      private $value;
      public function __construct($value = null) {
          $this->value = $value;
      }
    }
    // b.php
    namespace Another;
    use MyExample\MyAttribute;
    #[MyAttribute]
    #[\MyExample\MyAttribute]
    #[MyAttribute(1234)]
    #[MyAttribute(value: 1234)]
    #[MyAttribute(MyAttribute::VALUE)]
    #[MyAttribute(array("key" => "value"))]
    #[MyAttribute(100 + 200)]
    class Thing {
    }
    #[MyAttribute(1234), MyAttribute(5678)]
    class AnotherThing {
    }

    // --- reading Attributes with the Reflection API with getAttributes() method
    // returns an array of ReflectionAttribute instances that can be queried for
    // attribute name, arguments and to instantiate an instance of the represented attribute.
    // increases control to handle errors regarding missing attribute classes, mistyped or missing arguments
    // oly after calling ReflectionAttribute::newInstance(),
    // objects of the attribute class are instantiated and the correct matching of arguments is validated, not earlier.
    #[Attribute]
    class MyAttribute {
      public $value;
      public function __construct($value) {
        $this->value = $value;
      }
    }
    #[MyAttribute(value: 1234)]
    class Thing {
    }
    function dumpAttributeData($reflection) {
      $attributes = $reflection->getAttributes();
      foreach ($attributes as $attribute) {
        var_dump($attribute->getName());
        var_dump($attribute->getArguments());
        var_dump($attribute->newInstance());
      }
    }
    dumpAttributeData(new ReflectionClass(Thing::class));
    /*
      string(11) "MyAttribute"
      array(1) {
        ["value"]=>
        int(1234)
      }
      object(MyAttribute)#3 (1) {
        ["value"]=>
        int(1234)
      }
    */
    // reading specific attributes
    function dumpMyAttributeData($reflection) {
        $attributes = $reflection->getAttributes(MyAttribute::class);
        foreach ($attributes as $attribute) {
           var_dump($attribute->getName());
           var_dump($attribute->getArguments());
           var_dump($attribute->newInstance());
        }
    }
    dumpMyAttributeData(new ReflectionClass(Thing::class));
  
fibers

    // full-stack, interruptible functions
    // may be suspended from anywhere in the call-stack, pausing execution within the fiber until the fiber is resumed at a later time.
    // pause the entire execution stack, so the direct caller of the function does not need to change how it invokes the function.
    // Fiber::suspend() - may be in a deeply nested function or not even exist at all
    //  resumed with any value using Fiber::resume()
    // or by throwing an exception into the fiber using Fiber::throw()
    // currently it is not possible to switch fibers in the destructor of an object
    $fiber = new Fiber(function (): void {
       $value = Fiber::suspend('fiber');
       echo "Value used to resume fiber: ", $value, PHP_EOL;
    });
    $value = $fiber->start();
    echo "Value from fiber suspending: ", $value, PHP_EOL; // Value from fiber suspending: fiber
    $fiber->resume('test'); // Value used to resume fiber: test
  
enumerations

    // or "Enums" allow to define a custom type that is limited to one of a discrete number of possible values
    // itself is a class, and its possible cases are all single-instance objects of that class
    // Enum cases are valid objects and may be used anywhere an object may be used, including type checks.
    enum Suit
    {
      case Hearts;
      case Diamonds;
      case Clubs;
      case Spades;
    }
    function pick_a_card(Suit $suit) { ... }
    $val = Suit::Diamonds; // OK
    pick_a_card($val); // OK
    pick_a_card(Suit::Clubs); // TypeError: pick_a_card(): Argument #1 ($suit) must be of type Suit, string given
    pick_a_card('Spades');
    $a = Suit::Spades;
    $b = Suit::Spades;
    $a === $b; // true
    $a instanceof Suit;  // true
    // read-only property "name", case-sensitive name of the case itself
    Suit::Spades->name; // prints "Spades"
    // backed enumerations - scalar equivalent for an Enumeration, the syntax is as follows:
    enum Suit: string {
      case Hearts = 'H';
      case Diamonds = 'D';
      case Clubs = 'C';
      case Spades = 'S';
    }
    // backed Cases have an additional read-only property "value"
    print Suit::Clubs->value; // prints "C"
    // to add some constants in Enum cases, using class constants:
    Here's an example :
    class Something {
      const PARENT = 'parent';
    }
    enum MyEnum:string {
      case firstChild = Something::PARENT . '_child1';
      case secondChild = Something::PARENT . '_child2';
    }
    echo MyEnum::firstChild->value; //print"parent_child1"

    // --- may contain methods, and may implement interfaces
    // if an Enum implements an interface, then any type check for that interface will also accept all cases of that Enum
    interface Colorful {
        public function color(): string;
    }
    enum Suit implements Colorful {
      case Hearts;
      case Diamonds;
      case Clubs;
      case Spades;
      public function color(): string { // ulfills the interface contract
        return match($this) {
          Suit::Hearts, Suit::Diamonds => 'Red',
          Suit::Clubs, Suit::Spades => 'Black',
        };
      }
      public function shape(): string { // not part of an interface; that's fine
        return "Rectangle";
      }
    }
    function paint(Colorful $c) { ... }
    paint(Suit::Clubs);  // Works
    print Suit::Diamonds->shape(); // prints "Rectangle"
    interface Colorful {
      public function color(): string;
    }
    enum Suit: string implements Colorful {
      case Hearts = 'H';
      case Diamonds = 'D';
      case Clubs = 'C';
      case Spades = 'S';
      public function color(): string { // fulfills the interface contract
        return match($this) {
          Suit::Hearts, Suit::Diamonds => 'Red',
          Suit::Clubs, Suit::Spades => 'Black',
        };
      }
    }
    // inside a method, the $this variable is defined and refers to the Case instance
    // above hierarchy is logically similar to the following class structure (although this is not the actual code that runs):
    interface Colorful {
      public function color(): string;
    }
    final class Suit implements UnitEnum, Colorful {
      public const Hearts = new self('Hearts');
      public const Diamonds = new self('Diamonds');
      public const Clubs = new self('Clubs');
      public const Spades = new self('Spades');
      private function __construct(public readonly string $name) {}
      public function color(): string {
        return match($this) {
          Suit::Hearts, Suit::Diamonds => 'Red',
          Suit::Clubs, Suit::Spades => 'Black',
        };
      }
      public function shape(): string {
        return "Rectangle";
      }
      public static function cases(): array {
        // Illegal method, because manually defining a cases() method on an Enum is disallowed.
        // See also "Value listing" section.
      }
    }
    // methods may be public, private, or protected
    // in practice private and protected are equivalent as inheritance is not allowed

    // --- static methods - primarily for alternative constructors:
    enum Size {
      case Small;
      case Medium;
      case Large;
      public static function fromLength(int $cm): static {
        return match(true) {
          $cm < 50 => static::Small,
          $cm < 100 => static::Medium,
          default => static::Large,
        };
      }
    }

    // --- constants - may be public, private, or protected,
    // in practice private and protected are equivalent as inheritance is not allowed
    enum Size {
        case Small;
        case Medium;
        case Large;

        public const Huge = self::Large;
    }

    // before PHP 8.1
    class Method {
      public const GET = 'GET';
      public const POST = 'POST';
      public const PUT = 'PUT';
      public const PATCH = 'PATCH';
      public const DELETE = 'DELETE';
    }
    // PHP 8.1
    enum Method: string {
      case GET = 'GET';
      case POST = 'POST';
      case PUT = 'PUT';
      case PATCH = 'PATCH';
      case DELETE = 'DELETE';
    }
    trait SendsRequests {
      // before PHP 8.1
      public function send(string $method, string $uri, array $options = []): Response {
        if (! in_array($method, ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'])) {
          throw new InvalidArgumentException(
            message: "Method [$method] is not supported.",
          );
        }
        return $this->buildRequest()->send(
          method: $method,
          uri: $uri,
          options: $options,
        );
      }
      // PHP 8.1
      public function send(Method $method, string $uri, array $options = []): Response {
        return $this->buildRequest()->send(
          method: $method->value,
          uri: $uri,
          options: $options,
        );
      }
    }

    // ... Traits, using in constants, ...
  
old and new evaluation of indirect expressions
left-to-right vs. right-to-left
Expression PHP 5 interpretation PHP 7 interpretation
$$foo['bar']['baz'] ${$foo['bar']['baz']} ($$foo)['bar']['baz']
$foo->$bar['baz'] $foo->{$bar['baz']} ($foo->$bar)['baz']
$foo->$bar['baz']() $foo->{$bar['baz']}() ($foo->$bar)['baz']()
Foo::$bar['baz']() Foo::{$bar['baz']}() (Foo::$bar)['baz']()

Back to Main Page