I am finding it a bit difficult to understand Facades. Particularly how to find the underlying class name/location from a facade name. I have gone through the documentation but still not clear. For example, when using Auth::login()
, i found that there is no login() method in the Auth facade.
class Auth extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'auth';
}
/**
* Register the typical authentication routes for an application.
*
* #return void
*/
public static function routes()
{
static::$app->make('router')->auth();
}
}
The Auth facades getFacadeAccessor() method returns a string auth. But to which auth class should i be looking at? How to resolve the actual class?
Thanks,
You can use getFacadeRoot()
For example
$object = Auth::getFacadeRoot() // Illuminate\Auth\AuthManager instance
or to get the fully qualified class name
$class = get_class(Auth::getFacadeRoot()) // 'Illuminate\Auth\AuthManager'
Also you can use the container to resolve a class by it's accessor. This is what Laravel does under the hood when resolving a Facade.
$object = resolve('auth'); // Illuminate\Auth\AuthManager instance
Somewhere in a Serviceprovider the auth key is registered to something. For the auth key that's in vendor/laravel/frameworksrc/Illuminate/Auth/AuthServiceProvider.php. You can see that in the registerAuthenticator() method, the auth key is registered to the Illuminate\Auth\AuthManager with a singleton pattern.
The container has several ways to bind a key to a specific class. methods like bind and singleton for example. Facades are just an extra class to call the main class statically from the root namespace.
If you want to check out which class is used, you can use the following code: get_class(resolve('auth')). Ofcourse, you can replace auth with any string you want to check.
Bonus: I think you can override this behaviour by registering your own manager in some kind of way. I would advise you to extend the normal AuthManager and overwrite the methods that you want to see changed.
One option is to utilise the #see annotations on the facade
/**
* #see \Illuminate\Auth\AuthManager
* #see \Illuminate\Contracts\Auth\Factory
* #see \Illuminate\Contracts\Auth\Guard
* #see \Illuminate\Contracts\Auth\StatefulGuard
*/
class Auth extends Facade
Usually the method should exist on these classes/interfaces
For example, Auth::check() exists on \Illuminate\Contracts\Auth\Guard::check().
If you use an editor that allows you to follow through these definitions it can be a bit easier to traverse. Usually there's only one #see annotation so it's pretty easy to find the class.
You can use getFacadeRoot() on the package/service to get its object:
For example, I write a helper function to get Facade object so I can use that Cart object with ease from anywhere:
function cart() {
return \Gloudemans\Shoppingcart\Facades\Cart::getFacadeRoot();
}
Related
I'm fairly new to Laravel and I want to add a method to a vendor class. I'm sure it's just my unfamiliarity with how Laravel works, so I'm hoping there's a pretty easy solution.
I've installed a package (https://github.com/kawax/laravel-amazon-product-api) and want to add a new method that I can call like:
use App\Repositories\AmazonSearch\AmazonSearch;
$response = AmazonSearch::alter('All');
So I created a new folder app/Repositories/AmazonSearch and extended the AmazonClient class:
<?php
namespace App\Repositories\AmazonSearch;
use Revolution\Amazon\ProductAdvertising\AmazonClient;
class AmazonSearch extends AmazonClient {
/**
* {#inheritdoc}
*/
public function alter(...)
...
}
I guess I'm not sure on exactly what I need to do to be able to have this class instantiated like the original and use this new method.
Should I be creating a new service provider that would instantiate the new class? Can the existing one (https://github.com/kawax/laravel-amazon-product-api/blob/master/src/Providers/AmazonProductServiceProvider.php) just be extended?
There's some other answers here but many of them are for older Laravel versions. I'm not sure how to approach it the Laravel 8 way.
And I'm still fuzzy on how Laravel does all this, so thanks for your patience and any assistance you can provide.
EDIT: Well, I just renamed the class to ExtendedAmazonClient and added a facade and it seems to work now.
namespace App\AmazonSearch;
use Illuminate\Support\Facades\Facade;
use Revolution\Amazon\ProductAdvertising\AmazonClient;
class AmazonSearch extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return ExtendedAmazonClient::class;
}
}
trait Alter {
/**
* {#inheritdoc}
*/
public function alter(string $str) {
dd($str);
}
}
class ExtendedAmazonClient extends AmazonClient {
use Alter;
}
Can someone explain to me why the facade was the key?
To answer my own question for anyone else in the future: Facades is another way to use classes without manually creating an object. They are just a shortcut to classes registered by Laravel container.
https://stackoverflow.com/a/48843414/8749507
I have a project on Laravel and need to do refactoring. I've read about Service provider and Dependency injection and have some questions.
This is a short structure: user model, event model, favorite user model and etc. Also, there are controllers for all models. Every event has a creator and client (user relationship). In every class, I am injecting appropriate service: User Service, Event service, Favorite user service and etc.
Let's consider the example - I want to delete the user:
class UserController extends Controller
{
/**
* #var UserService $userService
*/
protected $userService;
/**
* UserController constructor.
* #param UserService $userService
*/
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
protected function delete(int $id)
{
$user = User::find($id);
if ($user) {
$this->userService->setUser($user);
$this->userService->delete();
}
}
Inside User service, I am processing user deleting - update the appropriate field. Also, I need to cancel all user events and delete favorite users.
My question is where should I do it? Should I inject event and favorite user service in UserController or in UserService? Or maybe there is a better way to do this action. Thx in advance
Seems like you have many actions depending on deleting user, so I would consider using Events and inside each listener handle the specifics of it.
class UserController extends Controller
{
/**
* #var UserService $userService
*/
protected $userService;
/**
* UserController constructor.
* #param UserService $userService
*/
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
protected function delete(int $id)
{
if(!$this->userService->delete($id)) {
// return some error;
}
event(new UserWasRemoved($id));
// return success response
}
class DeleteUserService {
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function delete($id){
return $this->user->delete($id);
}
}
// app/Providers/EventServiceProvider
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
UserWasRemoved::class => [
CancelUserEvents::class,
RemoveUserFavorites::class,
// etc...
],
];
}
if deleting a user is much code, I will create DeleteUserService class which will contain all the code needed to delete a user and the effects of the delete.
class DeleteUserService {
public function __construct(int $userId)
{
$this->userId = $userId;
}
public function delete(){
$this->deleteUser();
$this->updateAppropriateFields(); // of course the name should be clearer
$this->deleteEvents();
$this->deleteFavoriteUser();
...
}
private function deleteUser(){...}
private function updateAppropriateFields(){...}
private function deleteEvents(){...}
private function deleteFavoriteUser(){...}
...
}
and in your controller either you inject the service or instantiate a new instance in the controller method
class UserController extends Controller
{
...
public function delete(int $id)
{
$user = User::findOrFail($id);
$deleteService = new DeleteUserService($user->id);
$deleteService->delete();
}
}
it's always a good idea to break your large function into one or more classes.
I suggest you abandon your approach to using services like this. Everything that you implement with services has already been implemented in Laravel, only even easier. You are now implementing more cumbersome logic on top of a simple, ready-made one.
For each object of your subject area (user, event, favorite user) add model class. Add in them the information of tables, the data from which belong to them - unless of course you use relational storage Eloquent Model Conventions. Here I have a question for you - does the favorite user entity need a separate class? If the User and the FavoriteUser have the same characteristics (that is, class members in the implementation), then there is no need to distribute them into different classes, and it is enough to add an additional isFavourite() (bool) attribute - in the class and in the table.
Implement the necessary methods in the controllers for each of your model classes as described in the documentation Defining Controllers. Depending on the type of the client part, the return of the response can be either JSON for the RESTful API, or a blade template with the transmitted data Views. Here, in the controller, you should implement a method to delete the model.
If you do not want the logic to be similar, that is, get rid of the similar methods all(), get(), post(), put(), delete() and others for UserController, for EventController, ... (with the exception of model classes - which will be different), then I advise you use the following architectural trick (this is optional, of course). Develop a universal layer - a class of a universal model, a class of a universal controller, a class of a universal model repository (if you use it in development). And in the controller, describe the common logic for all model classes, all(), get(), post(), put(), delete(). And then inherit each concrete class of the model from the universal, each concrete class of the controller from the universal - and so on. But!
In a concrete class of the model, it is necessary, for example, in an array, to list the attributes of the relational storage table, where you get the data from; it is also necessary to specify the name of the class in the variable - so that the controller can understand which class it should work with.
And in the controller in any way pass data about the model class - for example, using DependencyInjection Dependency Injection & Controllers.
With this approach, the classes of concrete controllers become thin, and the increase in code in them occurs only due to the redefinition of universal methods or the implementation of custom ones.
The advantage of this approach is also that there is no need to add routes of a similar structure. For example, a universal route will suffice for you
Route::get('{entity}/{id}', function ($entity, $id) {
$module = ucfirst($entity);
Route::get("{$entity}/{$id}", "{$module}Controller#get");
});
instead of many of the same type
Route::get('user/{id}', 'UserController#get');
Route::get('event/{id}', 'EventController#get');
and the like.
We need to use Repository in laravel application. We want create Two important things one is Repository Interface and another one Repository class
My doubt is Why Repository interface and Repository class register to service provider
I removed repository interface and class from service provider
show below error
"Target [App\Repository\UserInterface] is not instantiable while building "
<?php
namespace App\Repository\user;
use Illuminate\Support\ServiceProvider;
class UserRepoServiceProvide extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
// $this->app->bind('App\Repository\UserInterface', 'App\Repository\user\UserRepository');
}
}
An Interface class is just a definition of methods (without their body), hence is not instantiable. That means you can't do a new App\Repository\UserInterface().
Somewhere in your code you have a method (or maybe a constructor?) that takes a UserInterface dependency, something like
public function myMethod(UserInterface $repository) {
...
}
// or
public function __construct(UserInterface $repository) {
...
}
If you remove the binding, Laravel will try to instantiate an UserInterface and that will result in the error you get.
When working with interfaces you have always to bind() them with concrete classes.
I have a question, why did you remove the binding from ServiceProvider?
Documentation on laravel.com is not sufficient. Can any one guide me through how to How To Create contracts in Laravel from scratch.
I need implementation of Contracts in Laravel. Right now, I'm using Laravel 5.4
Contract is just a fancy name for php interfaces. We have being using them all along and its not a new thing.
Contracts/Interfaces help us to maintain a loosely coupled code base. See the example from doc below.
<?php
namespace App\Orders;
class Repository
{
/**
* The cache instance.
*/
protected $cache;
/**
* Create a new repository instance.
*
* #param \SomePackage\Cache\Memcached $cache
* #return void
*/
public function __construct(\SomePackage\Cache\Memcached $cache)
{
$this->cache = $cache;
}
/**
* Retrieve an Order by ID.
*
* #param int $id
* #return Order
*/
public function find($id)
{
if ($this->cache->has($id)) {
//
}
}
}
Here when ever the Repository instantiate we should give a \SomePackage\Cache\Memcached instance in order for code to work. Hence our code is tightly coupled with \SomePackage\Cache\Memcached. Now look at below code.
<?php
namespace App\Orders;
use Illuminate\Contracts\Cache\Repository as Cache;
class Repository
{
/**
* The cache instance.
*/
protected $cache;
/**
* Create a new repository instance.
*
* #param Cache $cache
* #return void
*/
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
}
Same thing but now we just need to provide some cache interface. And behind the scene you could have done something like this.
<?php
namespace App\Orders;
use Illuminate\Contracts\Cache\Repository as Cache;
class RedisCache implements Cache {
//
}
When above Repository instantiate, php will look at the Illuminate\Contracts\Cache\Repository and It has been implemented by RedisCache class.
I'm afraid Gayan's answer needs further elaboration to hit Rajan's question.
Yes Gayan is correct that creating a Contract class basically means creating a php interface.
Continuing the Cache example above, if we look into its source code (you can find it at this Github repo file), we can see something like this
<?php
namespace Illuminate\Contracts\Cache;
use Closure;
interface Repository
{
/**
* Determine if an item exists in the cache.
*
* #param string $key
* #return bool
*/
public function has($key);
/**
* Retrieve an item from the cache by key.
*
* #param string $key
* #param mixed $default
* #return mixed
*/
public function get($key, $default = null);
// the rest...
}
If we are using this interface in our laravel app, it is said to be a "Contract". It is declaring what methods/properties a class should have if it implements this interface. For example in our app...
<?php
namespace App\Whatever;
use Illuminate\Contracts\Cache\Repository;
class Foo implements Repository {
//
}
Then class Foo will need to have methods has and get in order to fulfil what has been stated in the Repository contract.
Hi I am creating a custom cache service class which will abstract the caching layer out of my repository. However I am running into some trouble as I'm getting this error:
Argument 1 passed to Task::__construct() must implement interface MyApp\Cache\CacheInterface, none given, called in /var/www/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php on line 792 and defined
My class is as so follows:
<?php namespace MyApp\Cache;
use Illuminate\Cache\CacheManager;
class CacheService {
/**
* #var Illuminate\Cache\CacheManager
*/
protected $cache;
/**
* #var integer
*/
protected $minutes;
/**
* Construct
*
* #param Illuminate\Cache\CacheManager $cache
* #param string $tag
* #param integer $minutes
*/
public function __construct(CacheManager $cache, $minutes = 60)
{
$this->cache = $cache;
$this->tag = $tag;
$this->minutes = $minutes;
}
/**
* Get
*
* #param string $key
* #return mixed
*/
public function get($key)
{
return $this->cache->tags($this->tag)->get($key);
}
/**
* Put
*
* #param string $key
* #param mixed $value
* #param integer $minutes
* #return mixed
*/
public function put($key, $value, $minutes = null)
{
if( is_null($minutes) )
{
$minutes = $this->minutes;
}
return $this->cache->tags($this->tag)->put($key, $value, $minutes);
}
/**
* Has
*
* #param string $key
* #return bool
*/
public function has($key)
{
return $this->cache->tags($this->tag)->has($key);
}
}
And in my model I have the following;
<?php
use Abstracts\Model as AbstractModel;
use Illuminate\Support\Collection;
use CMS\APIv2\Objects\Entity;
use MyApp/Cache\CacheInterface;
class SprintTask extends AbstractModel
{
/**
* #var CacheInterface
*/
protected $cache;
public function __construct(CacheInterface $cache)
{
$this->cache = $cache;
}
public static function scopegetAssignedSprint($id) {
$key = md5('id.'.$id.get_class());
if($this->cache->has($key))
{
return $this->cache->get($key);
}
$user = static::where('uid', $id)->lists('sprint_id');
$this->cache->put($key, $user);
return $user;
}
And I have a cache service provider which is as follows;
<?php
namespace MyApp\Cache;
use MyApp\Cache\CacheInterface;
use Illuminate\Support\ServiceProvider;
class CacheServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = false;
/**
* Register
*/
public function register()
{
$this->app->bind
('MyApp\Cache\CacheInterface',
'MyApp\Cache\CacheService');
}
}
Any ideas how I can setup this service provider correctly to be used in any mode/controller/repo etc??
have you tried to do it like this :
<?php
namespace MyApp\Cache;
use MyApp\Cache\CacheInterface;
use Illuminate\Support\ServiceProvider;
use Illuminate\Cache\CacheManager;
class CacheServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = false;
/**
* Register
*/
public function register()
{
$this->app->bind('MyApp\Cache\CacheInterface',function(){
return new CacheService(CacheManager $cache);
});
}
}
I just updated it bases on Trip comment
When service container tries to instantiate TaskRepository, it sees that one of its constructor arguments is an object of class CacheService. Therefore it first tries to instantiate this object so that it can be later passed to TaskRepository constructor.
Your CacheService defines two required arguments. When Container tries to instantiate CacheService, it needs to provide values for both attributes and pass them to the constructor. Service container normally uses the type hint of constructor arguments to identify what service should be injected. In your case you require $tag variable to be passed, but as it has no type hint in constructor signature, therefore service container has no idea what should be passed to the constructor.
That's why you're getting the error - it simply says that service container is not able to resolve one of required dependencies of CacheService class.
There are multiple solutions to that problem.
First of all, you'll need to add a type hint to $tags argument.
If $tags is an object of some class that service container is able to instantiate, add a type hint to CacheService constructor signature.
If instantiating $tags object is something that you want to handle yourself, create the object in one of your service providers and bind it using container's bind() method.
If $tags is soomething that cannot be managed and injected by the container, you'll need to instantiate CacheService yourself and bind it using bind().
If $tags is something that cannot be managed by service container, e.g. array, you'll need to instantiate TaskRepository yourself, not via the service container.
You can read more about dependency injection in Laravel here: http://laravel.com/docs/5.0/container
The short version is that you can't use dependency injection on an Eloquent model. The IoC magic doesn't work on them, and since your AbstractModel extends the Eloquent Model class, the IoC magic doesn't work here. (Partial explanation by Taylor Otwell -- https://github.com/laravel/framework/issues/3862#issuecomment-37364543)
There are a couple of ways to get this basic idea to work:
(1) Use a Repository for the Model and inject your CacheService there. Since Eloquent is a repository-like ORM, this can be confusing and tedious.
(2) Register your CacheService through a new Facade and use it like Laravel's built-in Cache facade, directly in your model without injection -- MyCache::has($key). It can be set up as a Singleton if you'll never need to connect to two different caches. (L4: http://laravel.com/docs/4.2/facades, L5: http://laravel.com/docs/5.1/facades)
(3) Use a combination of #1 and #2, as outlined here: http://dfg.gd/blog/decoupling-your-code-in-laravel-using-repositiories-and-services
You're trying to inject an instance implementing CacheInterface into SprintTask, but in your service provider, you're providing an instance of CacheService which does not implement CacheInterface.
You need to implement that interface in CashService in order to be able to pass it to SpringTask:
class CacheService implements CacheInterface