An enhanced version of the Laravel container.

Related tags

Laravel php laravel
Overview

laravel-lec

Laravel Enhanced Container

Latest Version on Packagist Total Downloads GitHub Tests Action Status PHP CS Fixer PHP CodeSniffer PHPStan Psalm

This package provides syntax sugar for the Laravel container calls and bindings, automatic resolution of bound implementation, method forwarding, and an enhanced version of the Laravel method binding feature.

The package requires PHP 8.0 and Laravel 8.x. Future versions of PHP and Laravel will be maintained.

Installation

You can install the package via composer:

composer require michael-rubel/laravel-enhanced-container

Usage

Basic binding with new syntax

bind(ServiceInterface::class)->to(Service::class);

As singleton:

bind(ServiceInterface::class)->singleton(Service::class);

As scoped instance:

bind(ServiceInterface::class)->scoped(Service::class);

Method binding

Assuming that is your function in the service class:

class Service
{
    public function yourMethod(int $count): int
    {
        return $count;
    }
}

Perform the call to your service through container:

call(Service::class)->yourMethod(100)

You can pass it as $this or basic class object too:

call(new Service())->yourMethod(100)

Then override the method behavior in any place of your app:

bind(Service::class)->method()->yourMethod(function ($service, $app, $params) {
    return $service->yourMethod($params['count']) + 1;
});

Alternative syntax:

bind(Service::class)->method('yourMethod', function ($service, $app, $params) {
    return $service->yourMethod($params['count']) + 1;
});

The result next call: 101

You can easily mock the methods in your tests as well, and it counts as code coverage. 😉

For example:

bind(ServiceInterface::class)->to(Service::class);
bind(ServiceInterface::class)->method(
    'externalApiRequestReturnsFalse',
    fn () => false
);

$service = call(ServiceInterface::class);

$call = $service->externalApiRequestReturnsFalse();

$this->assertFalse($call);

Remember that you need to use call() to method binding to work. It returns the instance of CallProxy. If you rely on interfaces, the proxy will automatically resolve bound implementation for you, no need to do it manually.

Method forwarding

This feature automatically forwards the method when it doesn't exist in your base class to another class, if the namespace/classname structure is met.

Usual use case: if you have some kind of Service or Domain, which contains business or application logic, then some kind of Repository or Builder, which contains your database queries, but you don't want your controllers (or View/Livewire components) to be dependent on the repositories directly, and don't want to write the "proxy" methods in the Service that references the Repository when it comes to just fetch the data without any additional operations.

To enable this feature, publish the config:

php artisan vendor:publish --tag="enhanced-container-config"

Then turn forwarding_enabled option on and set the class names that met your application structure.

Assuming your structure is:

Logic:
- App/Services/Users/UserService
Queries: 
- App/Repositories/Users/UserRepository

Then your classes:

class UserService
{
    public function someMethod(): bool
    {
        return true;
    }
}

class UserRepository
{
    public function yourUser(): bool
    {
        return true;
    }
}

Then call the method to fetch the user:

call(UserService::class)->yourUser()

The result will be true despite the method is missing in UserService. If you put the same method in the UserService, it will fetch the result from the service itself.

  • Note: if you use PHPStan/Larastan you'll need to add the @method docblock to the service to make it static-analyzable, otherwise it will return an error that the method doesn't exist in the class.

Testing

composer test
Comments
  • [9.x] Chained forwarding

    [9.x] Chained forwarding

    About

    This PR introduces a new version of LEC package with code niceties and new features.

    Instead of relying on the naming conventions, we can now explicitly define the classes we want to forward, and much to say, we can now chain it. 🔥

    How it works?

    We can enable the forwarding and define which class to assign in case of missing methods or properties in the base class.

    Define the forwarding:

    Forwarding::enable()
        ->from(UserService::class)
        ->to(UserRepository::class)
        ->from(UserRepository::class)
        ->to(UserModel::class);
    

    Make a proxy:

    $proxy = call(UserService::class);
    

    Call an existing method:

    $proxy->existingMethod();
    
    // Returns result from the `UserService::class`
    

    Call a non-existing method:

    $proxy->nonExistingMethod();
    
    // Returns result from the `UserRepository::class`, because of forwarding defined above.
    

    Call a non-existing method, but that doesn't exist also in UserRepository::class:

    $proxy->nonExistingInRepositoryMethod();
    
    // Returns result from the `UserModel::class`, because of chained forwarding.
    

    So, why?

    • In the first call, we received the result from UserService, because the method exists and it is expected.
    • In the next call, the proxy managed to replace the internal call instance with UserRepository, because of the forwarding defined before.
    • In the final call, the proxy swapped the internal instance to UserModel, because the method also missing in UserRepository (previous instance).

    Use cases?

    Still, I'm too lazy to think where to put a query. I may do that in the model, but then move it to the service if additional logic arrives. Then all calls stay untouched. I can manage forwardings at any time and without relying on naming conventions. Separation of concerns matters. 🙂

    Caveats

    Always take attention to which call instance you are working currently when calling methods, accessing, or assigning properties. If some of them don't exist, the proxy will swap an instance and you'll lose the internal state. I'm thinking about storing a previous instance or warning a developer in such a case, we'll see if it fits reality.

    enhancement 
    opened by michael-rubel 2
  • Bind is not instantiable

    Bind is not instantiable

    Hey,

    I am using PHP 8.0.11 and Laravel v8.62.0.

    After installing this package I changed my AppServiceProvider from:

    $this->app->bind(
        ServerOrderInterface::class,
        FakeServerOrderService::class
    );
    

    to:

    bind(ServerOrderInterface::class)->to(FakeServerOrderService::class);
    

    And basically everything broke with:

    Illuminate\Contracts\Container\BindingResolutionException 
    
    Target [MichaelRubel\EnhancedContainer\Bind] is not instantiable.
    
    opened by twdnhfr 2
  • Drop support of syntax sugar

    Drop support of syntax sugar

    About

    We decided to drop support of syntax sugar in this package and scope more on important features: method binding, forwarding, and additional methods in the container.

    These changes will ship as a major update.


    opened by michael-rubel 0
  • Add `remember` method to `Container`

    Add `remember` method to `Container`

    About

    Adds remember method allowing to resolve or execute the callback & bind results to the service container.

    app()->remember('key', fn () => 'result');
    

    enhancement 
    opened by michael-rubel 0
  • Add `makeOr` method

    Add `makeOr` method

    About

    This method will help us ensure our code will be executed if container resolution fails. This way we can bind stuff to the container "on-the-fly" even if it wasn't bound previously, or do something else.

    For example:

    app()->makeOr('company', function ($app) {
        $company = Company::firstWhere('uuid', request()->segment(1));
    
        $app->scoped('company', fn () => $company);
    
        return $company;
    });
    

    If we're resolving the service with parameters, we can do this:

    app()->makeOr(Service::class, ['dependency' => $dependency], function ($app) {
        //
    });
    

    There are a lot more use cases though. Optionally, it would have been binding results of executed queries to the container to avoid query duplication in the scope of the request lifecycle, if someone doesn't want to deal with the cache/invalidation issues, etc.


    Additionally, original Container tests were added. 🧪

    enhancement 
    opened by michael-rubel 0
  • Ability to set the previous instance & to disable forwarding in `CallProxy`

    Ability to set the previous instance & to disable forwarding in `CallProxy`

    About

    • Added ability to set the previous instance in the CallProxy
    • Added ability to disable forwarding on proxy level (isolated from the global scope)

    B/C breaks

    • Changed the type of $previous property from string to ?object.
    • Changed the method visibility in the InteractsWithContainer trait from public to protected to avoid autocomplete in IDE for these methods as they were meant only for internal purposes.
    enhancement 
    opened by michael-rubel 0
  • Use `Laravel Pint` instead of `PHP_CodeSniffer`

    Use `Laravel Pint` instead of `PHP_CodeSniffer`

    Logo Laravel Pint

    Introduction

    Laravel Pint is an opinionated PHP code style fixer for minimalists. Pint is built on top of PHP-CS-Fixer and makes it simple to ensure that your code style stays clean and consistent.

    enhancement 
    opened by michael-rubel 0
  • Resolve contextual binding in CallProxy

    Resolve contextual binding in CallProxy

    Topic

    This PR adds the ability to resolve contextual bindings in any available class method based on the third parameter passed to the proxy.

    singleton(BoilerplateInterface::class, BoilerplateService::class);
    
    // defines the implementation globally
    
    bind(BoilerplateInterface::class)
        ->contextual(TestService::class)
        ->for(BoilerplateServiceResolvesContextualInMethod::class);
    
    // defines contextual implementation
    

    In BoilerplateServiceResolvesContextualInMethod class:

    call(BoilerplateInterface::class, [], static::class);
    
    // static class identifies the context
    // returns contextual implementation (TestService::class)
    
    call(BoilerplateInterface::class);
    
    // in the same class
    // returns global implementation (BoilerplateService::class)
    

    Additional:

    • newgetInternal method to get proxy's internal properties.
    call(BoilerplateInterface::class)->getInternal('instance');
    

    B/C

    • getInternal method is considered as breaking if someone used the same method before this addition.
    enhancement 
    opened by michael-rubel 0
Releases(10.0.3)
  • 10.0.3(Dec 20, 2022)

    What's Changed

    • Avoid finding concrete when interface exists 🔧 by @michael-rubel in https://github.com/michael-rubel/laravel-enhanced-container/pull/16

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/10.0.2...10.0.3

    Source code(tar.gz)
    Source code(zip)
  • 10.0.2(Dec 20, 2022)

    What's Changed

    • Patch container 🔩 by @michael-rubel in https://github.com/michael-rubel/laravel-enhanced-container/pull/15

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/10.0.1...10.0.2

    Source code(tar.gz)
    Source code(zip)
  • 10.0.1(Dec 10, 2022)

    What's Changed

    • PHP 8.2 build 🐘 by @michael-rubel in https://github.com/michael-rubel/laravel-enhanced-container/pull/14

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/10.0.0...10.0.1

    Source code(tar.gz)
    Source code(zip)
  • 10.0.0(Nov 22, 2022)

    What's Changed

    • Drop support of syntax sugar by @michael-rubel in https://github.com/michael-rubel/laravel-enhanced-container/pull/13

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/9.3.0...10.0

    Source code(tar.gz)
    Source code(zip)
  • 9.3.0(Nov 16, 2022)

    What's Changed

    • Add remember method to Container by @michael-rubel in https://github.com/michael-rubel/laravel-enhanced-container/pull/12

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/9.2.2...9.3.0

    Source code(tar.gz)
    Source code(zip)
  • 9.2.2(Nov 15, 2022)

    What's Changed

    • Fix for non-associative arrays by @michael-rubel in https://github.com/michael-rubel/laravel-enhanced-container/pull/11

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/9.2.1...9.2.2

    Source code(tar.gz)
    Source code(zip)
  • 9.2.1(Nov 15, 2022)

    What's Changed

    • Introduce mutation testing by @michael-rubel in https://github.com/michael-rubel/laravel-enhanced-container/pull/10

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/9.2.0...9.2.1

    Source code(tar.gz)
    Source code(zip)
  • 9.2.0(Sep 13, 2022)

    What's Changed

    • Add makeOr method by @michael-rubel in https://github.com/michael-rubel/laravel-enhanced-container/pull/9

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/9.1.3...9.2.0

    Source code(tar.gz)
    Source code(zip)
  • 9.1.3(Aug 20, 2022)

    What's Changed

    • Fix dependency injection in forwarded objects by @michael-rubel in https://github.com/michael-rubel/laravel-enhanced-container/pull/8

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/9.1.2...9.1.3

    Source code(tar.gz)
    Source code(zip)
  • 9.1.2(Aug 20, 2022)

    What's Changed

    • Wrap to Closure if object passed to binding helpers by @michael-rubel in https://github.com/michael-rubel/laravel-enhanced-container/pull/7

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/9.1.1...9.1.2

    Source code(tar.gz)
    Source code(zip)
  • 9.1.1(Aug 20, 2022)

    What's Changed

    • Allow null in binding helpers by @michael-rubel in https://github.com/michael-rubel/laravel-enhanced-container/pull/6

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/9.1.0...9.1.1

    Source code(tar.gz)
    Source code(zip)
  • 9.1.0(Aug 12, 2022)

    What's Changed

    • Ability to set the previous instance & to disable forwarding in CallProxy by @michael-rubel in https://github.com/michael-rubel/laravel-enhanced-container/pull/5

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/9.0.0...9.1.0

    Source code(tar.gz)
    Source code(zip)
  • 9.0.0(Jul 8, 2022)

    What's Changed

    • Use Laravel Pint instead of PHP_CodeSniffer by @michael-rubel in https://github.com/michael-rubel/laravel-enhanced-container/pull/3
    • [9.x] Chained forwarding by @michael-rubel in https://github.com/michael-rubel/laravel-enhanced-container/pull/4

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/8.1.2...9.0.0

    Source code(tar.gz)
    Source code(zip)
  • 8.1.2(Jun 9, 2022)

    What's changed

    • Slightly refactored BindingBuilder class.
    • Added BC break tracker.

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/8.1.1...8.1.2

    Source code(tar.gz)
    Source code(zip)
  • 8.1.1(May 24, 2022)

    What's changed

    • The package now requires illuminate/contracts, illuminate/container and illuminate/support instead of requiring laravel/framework as a whole.

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/8.1.0...8.1.1

    Source code(tar.gz)
    Source code(zip)
  • 8.1.0(May 23, 2022)

    What's changed

    • Added container resolution for CallProxy directly in the helper.

    Full Changelog: https://github.com/michael-rubel/laravel-enhanced-container/compare/8.0.0...8.1.0

    Source code(tar.gz)
    Source code(zip)
  • 8.0.0(May 23, 2022)

    What's changed

    • Major update to avoid the requirement of registering LecServiceProvider for packages that rely on LEC.

    If you need Laravel 8.x support, you may include "michael-rubel/laravel-enhanced-container": "^6.4|^8.0" constraint to your composer.json. 6.4 for Laravel 8.x, 8.0 for Laravel 9.x.

    Source code(tar.gz)
    Source code(zip)
Owner
Michael Rubel
Certified Laravel Developer
Michael Rubel
A Docker container for Laravel web apps

Ubuntu Docker container for Laravel web applications Docker-laravel is a LEMP image for running Laravel web applications. It extends docker-base, whic

Mark Macdonald 121 Nov 18, 2022
Laravel Form builder for version 5+!

Laravel 5 form builder Form builder for Laravel 5 inspired by Symfony's form builder. With help of Laravels FormBuilder class creates forms that can b

Kristijan Husak 1.7k Dec 31, 2022
Laravel package to generate and to validate a UUID according to the RFC 4122 standard. Only support for version 1, 3, 4 and 5 UUID are built-in.

Laravel Uuid Laravel package to generate and to validate a universally unique identifier (UUID) according to the RFC 4122 standard. Support for versio

Christoph Kempen 1.7k Dec 28, 2022
This package wraps up the standalone executable version of the Tailwind CSS framework for a Laravel application.

Tailwind CSS for Laravel Introduction This package wraps the standalone Tailwind CSS CLI tool. No Node.js required. Inspiration This package was inspi

Tony Messias 240 Nov 19, 2022
Tool Berbasis Web Untuk membuat Laporan Praktik Kerja Lapangan Version 1.0

PKL-AUTO Tool Berbasis Web Untuk membuat Laporan Praktik Kerja Lapangan Version 1.0 Features : Online/Offline Mode Laporan langsung aja Tinggal Select

Ivan Dwi Yanto 1 Nov 26, 2021
Make your church sermons available for download. For the latest version, go:

Laravel Church Sermons App Laravel church sermons app is basically an app for churches to make available the messages preached in church for all membe

Dammy 28 Nov 13, 2022
Proxy validation or Proxy checker. Command line version

Proxy Checker Proxy validation or Proxy checker Install on desktop : Install XAMPP Added environment variable system path => C:\xampp\php download the

Alex 3 Jan 21, 2022
This is a Composer plugin that provides an automated version of Country Codes database.

Country Codes This is a Composer plugin that provides an automated version of Country Codes database. This database is include: country iso codes, cou

Robert Nicjoo 19 Sep 3, 2022
List of 77 languages for Laravel Framework 4, 5, 6, 7 and 8, Laravel Jetstream , Laravel Fortify, Laravel Breeze, Laravel Cashier, Laravel Nova and Laravel Spark.

Laravel Lang In this repository, you can find the lang files for the Laravel Framework 4/5/6/7/8, Laravel Jetstream , Laravel Fortify, Laravel Cashier

Laravel Lang 6.9k Jan 2, 2023
⚡ Laravel Charts — Build charts using laravel. The laravel adapter for Chartisan.

What is laravel charts? Charts is a Laravel library used to create Charts using Chartisan. Chartisan does already have a PHP adapter. However, this li

Erik C. Forés 31 Dec 18, 2022
Laravel Kickstart is a Laravel starter configuration that helps you build Laravel websites faster.

Laravel Kickstart What is Laravel Kickstart? Laravel Kickstart is a Laravel starter configuration that helps you build Laravel websites faster. It com

Sam Rapaport 46 Oct 1, 2022
Laravel User Activity Log - a package for Laravel 8.x that provides easy to use features to log the activities of the users of your Laravel app

Laravel User Activity Log - a package for Laravel 8.x that provides easy to use features to log the activities of the users of your Laravel app

null 9 Dec 14, 2022
Laravel Segment is an opinionated, approach to integrating Segment into your Laravel application.

Laravel Segment Laravel Segment is an opinionated, approach to integrating Segment into your Laravel application. Installation You can install the pac

Octohook 13 May 16, 2022
Laravel Sanctum support for Laravel Lighthouse

Lighthouse Sanctum Add Laravel Sanctum support to Lighthouse Requirements Installation Usage Login Logout Register Email Verification Forgot Password

Daniël de Wit 43 Dec 21, 2022
Bring Laravel 8's cursor pagination to Laravel 6, 7

Laravel Cursor Paginate for laravel 6,7 Installation You can install the package via composer: composer require vanthao03596/laravel-cursor-paginate U

Pham Thao 9 Nov 10, 2022
A package that uses blade templates to control how markdown is converted to HTML inside Laravel, as well as providing support for markdown files to Laravel views.

Install Install via composer. $ composer require olliecodes/laravel-etched-blade Once installed you'll want to publish the config. $ php artisan vendo

Ollie Codes 19 Jul 5, 2021
A light weight laravel package that facilitates dealing with arabic concepts using a set of classes and methods to make laravel speaks arabic

A light weight laravel package that facilitates dealing with arabic concepts using a set of classes and methods to make laravel speaks arabic! concepts like , Hijri Dates & Arabic strings and so on ..

Adnane Kadri 49 Jun 22, 2022
Jetstrap is a lightweight laravel 8 package that focuses on the VIEW side of Jetstream / Breeze package installed in your Laravel application

A Laravel 8 package to easily switch TailwindCSS resources generated by Laravel Jetstream and Breeze to Bootstrap 4.

null 686 Dec 28, 2022
Laravel Jetstream is a beautifully designed application scaffolding for Laravel.

Laravel Jetstream is a beautifully designed application scaffolding for Laravel. Jetstream provides the perfect starting point for your next Laravel application and includes login, registration, email verification, two-factor authentication, session management, API support via Laravel Sanctum, and optional team management.

The Laravel Framework 3.5k Jan 8, 2023