Without knowing how Laravel facades work, based on my PHP knowledge, I tried to extend Storage facade to add some new functionalities.
I have this code:
class MyStorageFacade extends Facade {
/**
* Get the binding in the IoC container
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'MyStorage'; // the IoC binding.
}
}
While booting service provider:
$this->app->bind('MyStorage',function($app){
return new MyStorage($app);
});
And facade is:
class MyStorage extends \Illuminate\Support\Facades\Storage{
}
When using it:
use Namespace\MyStorage\MyStorageFacade as MyStorage;
MyStorage::disk('local');
I get this error:
FatalThrowableError in Facade.php line 237: Call to undefined method Namespace\MyStorage\MyStorage::disk()
Also tried to extend MyStorage form Illuminate\Filesystem\Filesystem and got the same error in other way:
BadMethodCallException in Macroable.php line 74: Method disk does not exist.
Your MyStorage class needs to extend FilesystemManager not the Storage facade class.
class MyStorage extends \Illuminate\Filesystem\FilesystemManager {
....
}
A facade is just a convenience class that will convert a static call Facade::method to resolove("binding")->method (more or less). You need to extend from Filesystem, register that in IoC, keep your facade as it is, and use the Facade as a static.
The facade:
class MyStorageFacade extends Facade {
protected static function getFacadeAccessor()
{
return 'MyStorage'; // This one is fine
}
}
Your custom storage class:
class MyStorage extends Illuminate\Filesystem\FilesystemManager {
}
In any service provider (e.g. AppServiceProvider)
$this->app->bind('MyStorage',function($app){
return new MyStorage($app);
});
Then when you need to use it use it as:
MyStorageFacade::disk(); //Should work.
Related
I'm working with Laravel 5.8 and it's an Online Store project written by other programmers.
Basically I have faced something weird that never seen before.
Let's say we have this at a Controller method:
$payment = CourseRegistrationFacade::insertCourseRegisterInformation($courseId,$date,$memberId,$userId);
And then we goto the CourseRegistrationFacade and it goes like this:
class CourseRegistrationFacade extends BaseFacade
{
}
So the whole class is empty but it extends another Facade which is BaseFacade and it goes like this:
class BaseFacade extends Facade
{
protected static function getFacadeAccessor()
{
return static::class;
}
protected static function shouldProxyTo($class)
{
app()->bind(self::getFacadeAccessor(), $class)
}
}
And that's it !
I don't really know where the heal is insertCourseRegisterInformation !!
So if you know how this Facade works, please let me know...
Here is the full code of BaseFacade.php:
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class BaseFacade extends Facade
{
protected static function getFacadeAccessor()
{
return static::class;
}
public static function shouldProxyTo($class)
{
app()->bind(self::getFacadeAccessor(), $class);
}
}
Search in the code for:
CourseRegistrationFacade::shouldProxyTo(
Most likely in the service provider that line is somewhere registering that facade to some concrete implementation of a class. Then check the contents of the class (the argument passed to shouldProxyTo).
Inside that class there should be a method called insertCourseRegisterInformation.
The way facades work is they resolve the class out of the container and then call the method you call statically.
So for example, let's say you have a UserService.php with a method register() and that class is mapped to a UserServiceFacade.php. When you do UserServiceFacade::register(), __callStatic will get the facade accessor (actual class) from the container, then call the register() method of that class.
You can understand better by inspecting __callStatic inside Facade.php.
Essentially UserServiceFacade::register() is the same as doing:
$userService = app()->make(UserService::class);
$userService->register()
By using the facade you can hide the concrete implementation and could possibly switch it to something else in the future by just changing it in a single place.
I think there must be a Provider exists for that Facade which is initializing its associated class and insertCourseRegisterInformation method definition must be declared in it. Please find that provider and then you'll find its associated class from that Provider code. I think you can find all registered providers from config/app.php
These reference articles might help you.
Reference 1:
https://grafxflow.co.uk/blog/mvc/adding-global-custom-class-facades-laravel-5
Reference 2: http://www.expertphp.in/article/how-to-create-custom-facade-in-laravel-52
I encountered this issue using the repository pattern. Currently I use an interface, and a custom class to achieve it, then type-hint it into the controller's construct and because of Laravel, it will solve the repositories' dependencies automatically and recursively.
I also do this in a service provider:
$this->app->bind(path/to/repoInterface,path/to/implementationClass)
However, because of the way I coded these repositories, in order to avoid code duplication, I created an abstract class that has a common method to all these repositories. This class is as follows:
abstract class CommonRepo{
public function __construct(SomeModelClass model){}
public function commonMethod(){//Code here}
And my repositories have the following structure:
public class ExampleRepository extends CommonRepo implements ExampleRepositoryI{
public function __construct(){
parent::__construct();
}
}
Laravel doesn't like this, so its giving this error:
Argument 1 passed to path/to/repo/CommonRepo::__construct() must be an instance of path/to/model/SomeModelClass, none given, called in...
So, obviously is not resolving the dependency of the class CommonRepo, but it does resolve the dependencies on the normal repositories.
I'd like, if it's possible, to use type-hinting (the Laravel way) without having to do anything related to the new operator
How can I, then, resolve that class's dependencies ?
PD: Using Laravel 5.2
Parent constructor is called like normal function without touching dependency resolver so you should do one of two possibilities:
public class ExampleRepository extends CommonRepo implements ExampleRepositoryI
{
public function __construct(SomeModelClass $model){
parent::__construct($model);
}
}
or
public class ExampleRepository extends CommonRepo implements ExampleRepositoryI
{
public function __construct(){
parent::__construct(App::make(SomeModelClass::class));
}
}
nice question. I did some tinkering, though I don't know if this is what you're looking for. But you can dynamically create an instance of Eloquent model required by your repository class.
Let's say you have your User model class stored in app\Models\User.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
//
}
You then create a base abstract class for all of your repository classes: app\Repositories\BaseRepository.php. This is where you place all common functionalities for your repository classes. But rather than injecting the Eloquent instance through the constructor, you may add a method named getModel() to dynamically create an instance of Eloquent model for your repository.
<?php
namespace App\Repositories;
use ReflectionClass;
use RuntimeException;
use Illuminate\Support\Str;
abstract class BaseRepository
{
protected $modelNamespace = 'App\\Models\\';
public function getById($id)
{
return $this->getModel()->find($id);
}
public function getModel()
{
$repositoryClassName = (new ReflectionClass($this))->getShortName();
$modelRepositoryClassName = $this->modelNamespace . Str::replaceLast('Repository', '', $repositoryClassName);
if (! class_exists($modelRepositoryClassName)) {
throw new RuntimeException("Class {$modelRepositoryClassName} does not exists.");
}
return new $modelRepositoryClassName;
}
}
Now let's say you want to create a repository for your User model, and this user's repository must implement the following interface: app\Repositories\UserRepositoryInterface.php
<?php
namespace App\Repositories;
interface UserRepositoryInterface
{
public function getByEmail($email);
}
You create app\Repositories\UserRepository.php class and simply extend it from the BaseRepository class. Also don't forget to implement all specific implementations defined on UserRepositoryInterface.
<?php
namespace App\Repositories;
use App\Repositories\BaseRepository;
use App\Repositories\UserRepositoryInterface;
class UserRepository extends BaseRepository implements UserRepositoryInterface
{
public function getByEmail($email)
{
return $this->getModel()->where('email', $email)->firstOrFail();
}
}
This way you can bind the UserRepositoryInterface to it's implementation like so:
$this->app->bind(\App\Repositories\UserRepositoryInterface::class, \App\Repositories\UserRepository::class);
Finally you can freely inject the UserRepositoryInterface to a controller's constructor or methods. You can also resolve it via service container like this:
$userRepository = App::make(App\Repositories\UserRepositoryInterface::class);
$userRepository->getByEmail('john#example.com');
Of course there's a catch to this approach. The repository class should be started with the associated model, so the InvoiceRepository.php is dedicated for Invoice.php model class.
Hope this help!
This might help. You can listen in for when an object resolves and set attributes.
$this->app->resolving(CommonRepo::class, function ($object, $app) {
// Called when container resolves object of any type...
$object->commonObject = app(CommonObject::class);
});
Docs: https://laravel.com/docs/5.4/container#container-events
I am attempting to create a facade within laravel 4.1. I have created the facade, service provider and the class, to no avail. I followed numerous "how to's" including the advanced video for custom facades on Laracasts. No matter how many times I try, I end up with the exception of Non-static method Custom\Helpers\Helper::doSomething() should not be called statically
Here is my code...
HelpersServiceProvider.php
<?php namespace Custom\Helpers;
use Illuminate\Support\ServiceProvider;
class HelpersServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('trial','Custom\Helpers\Helper');
}
}
HelpersFacade.php
<?php namespace Custom\Facades;
use Illuminate\Support\Facades\Facade;
class Helper extends Facade {
protected static function getFacadeAccessor()
{
return 'trial';
}
}
Helpers.php
<?php namespace Custom\Helpers;
class Helper {
public function doSomething()
{
return 'Hello';
}
}
I add the service provider to my app.php file and register the facade alias
'Custom\Helpers\HelpersServiceProvider',
'Helper' => 'Custom\Facades\Helper',
Then when I try to access it via a Static call (yes, I know it's not really static) or via the service provider directly I get the exception error.
Scratching my head on this one...
It looks like you have an incorrectly named class (or file):
HelpersFacade.php
class Helper extends Facade {
Additionally, your Helper class is in Helpers.php. Those need to match, also.
I have a custom class App/Http/Responder, which had a few methods to build a specific JSON response back in my application. I want to test my controller in isolation, so I'm trying to inject my dependencies via the constructor.
My plan was to simply create a service provider, attach bind it to the $app and then, as per the docs, let it be automatically resolved:
public function register()
{
$this->app->bind('responder', function()
{
return new App\Http\Responder($this->app['cache'], $this->app['app'], new JsonResponse, $this->app['config']);
});
}
I then add this to my config/app.php.
Okay, so now my Responder and it's dependancies are bound to the app, as responder.
Now I thought I'd be able to inject Responder into my controller constructor, and Laravel would be able to automatically resolve this from the IoC container:
class AreasController extends BaseController {
protected $responder;
public function __construct(Responder $responder)
{
$this->responder = $responder;
}
However I get Class Responser does not exist.
The only way I can get it working, without using the App::make() Facade, is to inject the app into my controller:
use Illuminate\Foundation\Application as App;
class AreasController extends BaseController {
protected $app;
public function __construct(App $app)
{
$this->app = $app;
}
I can then do $this->app['responder']->method().
Obviously I'm missing something, but I want to keep away from using Facades in my app so I can test.
If you want to type hint classes to be resolved in the IOC container, you should bind the actual class name with namespace:
$this->app->bind('App\Http\Responder', function()
{
return new App\Http\Responder($this->app['cache'], $this->app['app'], new JsonResponse, $this->app['config']);
});
Technically the container would still resolve this class, because it's a concrete class that can be found, but the way you're doing allows to inject other IOC-bound resources, which is a good practice.
Then, when you wish to have this class injected for you, type hint the full path to the class as you normally would:
use App\Http\Responder;
class AreasController extends BaseController {
protected $responder;
public function __construct(Responder $responder)
{
$this->responder = $responder;
}
}
Also, for what it's worth, your error indicates that you misspelled "Responder" as "Responser".
I have a class called Messaging and I created a facade to using it like
Messaging::getConversationMessages($conv_id, $user_id);
I have followed all the instructions in this link below
How do I create a facade class with Laravel?
This is my MessagingServiceProvider calss below which does the binding
<?php
use Illuminate\Support\ServiceProvider;
class MessagingServiceProvider extends ServiceProvider {
/**
* Register the service provider.
*
* #return void
*/
public function register() {
App::bind('messaging', function()
{
return new \Messaging\Messaging;
}
);
}
}
Below is my facade class that I created for me to use it in the way I wanted to
<?php
use Illuminate\Support\Facades\Facade;
class Messaging extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor() { return 'messaging'; }
}
I have placed my MessagingServiceProvider.php inside a folder called serviceproviders inside app folder, and placed the messaging.php(the file containing the facade class) inside a folder called facade in the app folder and added them to auto load.
Below is the model class for the facade
<?php
namespace Messaging;
use Eloquent; // if you're extending Eloquent
class Messaging extends Eloquent {
...
}
After doing all this still I am getting an error "Non-static method Messaging\Messaging::getConversationMessages() should not be called statically, assuming $this from incompatible context"
You are not using your Facade. Try to namespace it and use a different name:
<?php namespace Messaging;
use Illuminate\Support\Facades\Facade;
class MessagingFacade extends Facade
{
...
}
Then you
composer dumpautoload
And try to use it this way:
Messaging\MessagingFacade::getConversationMessages()
If it works for you, you create an alias for it in app/config/app.php.
'Messaging' => 'Messaging\MessagingFacade',
And you should be able to use it as:
Messaging::getConversationMessages()
About namespaced classes, every time you need to a class from another namespace, you need to go root:
\DB::whatever();