I am using Laravel 5.2.45.
I am using dependency injection for my services.
Now, there is service A that needs service B, so I inject service B into A.
Also, there are methods in service B, that need service A, so I also injected service A into B.
However, this configuration seems to create a problem. This is my implementation:
class AService
{
/**
* #var BService
*/
protected $bService;
public function __construct(BService $bService) {
$this->bService = $bService;
}
}
class BService
{
/**
* #var AService
*/
protected $aService;
public function __construct(AService $aService) {
$this->aService = $aService;
}
}
First I noticed that my routes are not working, so I tried checking them with:
php artisan route:list
And I got:
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted
(tried to allocate 4096 bytes) in
/var/www/tacnet-plus/bootstrap/cache/compiled.php on line 1426
Segmentation fault (core dumped)
It seems that this configuration creates a memory overflow. I suspect that the first service creates the second and the second creates the first indefinitely.
I have had the same issue in the past with Java Spring, and I know you can inject your services there with a setter instead of the constructor, to avoid creating the dependency in the constructor and inject the service on demand through the setter. Is there something similar in Laravel?
It seems that there is nothing out of the box that will inject a service without using the constructor. However, I can change one of both services to inject the other service manually in a getter, instead of the constructor:
class AService
{
/**
* #var BService
*/
protected $bService;
public function __construct(BService $bService) {
$this->bService = $bService;
}
}
use App;
class BService
{
public function __construct() {
}
/**
* #return AService
*/
private function getAService() {
return App::make('Some\Namespace\AService');
}
}
Related
I am sorry if for someone the title of my question turns out to be very common, but the truth is that I have been trying for hours to obtain the expected result and I have not succeeded.
It happens, that I am developing a small package for Laravel, and I cannot perform a dependency injection in a method within a command that will contain the package.
Inside the directory structure of my package, I have the ServiceProvider
<?php
namespace Author\Package;
use Author\Package\Commands\BaseCommand;
use Author\Package\Contracts\MyInterface;
use Illuminate\Support\ServiceProvider;
class PackageServiceProvider extends ServiceProvider
{
/**
* The commands to be registered.
*
* #var array
*/
protected $commands = [
\Author\Package\Commands\ExampleCommand::class
];
/**
* Register services.
*
* #return void
*/
public function register()
{
if (! $this->app->configurationIsCached()) {
$this->mergeConfigFrom(__DIR__ . '/../config/package.php', 'package');
}
$this->app->bind(MyInterface::class, BaseCommand::class);
}
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
if ($this->app->runningInConsole()) {
$this->publishes([
__DIR__ . '/../config/package.php' => config_path('package.php')
], 'package-config');
$this->configureCommands();
}
}
/**
* Register the package's custom Artisan commands.
*
* #return void
*/
public function configureCommands()
{
$this->commands($this->commands);
}
}
As you can see from the register method, I am creating a binding for when it calls the MyInterface interface, it returns the concrete BaseCommand class
public function register()
{
...
$this->app->bind(MyInterface::class, BaseCommand::class);
}
The structure of the ExampleCommand file is as follows:
<?php
namespace Author\Package\Commands;
use Author\Package\Contracts\MyInterface;
use Illuminate\Console\Command;
class ExampleCommand extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'my:command';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Command Description';
/**
* Execute the console command.
*
* #return void
*/
public function handle(MyInterface $interface)
{
// TODO
}
}
But when I run the command, I get the following error:
TypeError
Argument 1 passed to Author\Package\Commands\ExampleCommand::handle() must be an instance of Author\Package\Contracts\MyInterface, instance of Author\Package\Commands\BaseCommand given
I wonder why dependency injection is not working, in essence it should inject the concrete BaseCommand class into the handle method of the ExampleCommand class, but it isn't. I would appreciate any help you can give me.
Your BaseCommand must implement the interface that you have typehinted for that handle method. Dependency Injection happens before the method is called, so the container resolved your binding (as it is trying to pass an instance of BaseCommand to the method call, handle) but the binding does not return something that implements that contract so PHP won't allow that to be passed for that argument since it doesn't match the type of the argument in the signature (does not implement the contract).
In short: if you are going to bind a concrete to an abstract, make sure the concrete is actually of the type you are binding it to.
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
I've set up a package in laravel 4 via the artisan workbench command. I created a facade class and followed this tutorial to come up with the following service provider, facade and root classes:
src/Spoolphiz/Infusionsoft/InfusionsoftServiceProvider.php:
namespace Spoolphiz\Infusionsoft;
use Illuminate\Support\ServiceProvider;
class InfusionsoftServiceProvider extends ServiceProvider {
protected $defer = false;
/**
* Bootstrap the application events.
*
* #return void
*/
public function boot()
{
$this->package('spoolphiz/infusionsoft');
}
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
// Register 'infusionsoft' instance container to our Infusionsoft object
$this->app['infusionsoft'] = $this->app->share(function($app)
{
return new Spoolphiz\Infusionsoft\Infusionsoft;
});
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides()
{
return array();
}
}
src/Spoolphiz/Infusionsoft/Facades/Facade.php:
namespace Spoolphiz\Infusionsoft\Facades;
use Illuminate\Support\Facades\Facade;
class Infusionsoft extends Facade {
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor() { return 'infusionsoft'; }
}
Finally I've set up the underlying class thats to be connected to the facade at src/Spoolphiz/Infusionsoft/Infusionsoft.php:
namespace Spoolphiz\Infusionsoft;
//use Spoolphiz\Infusionsoft\iSDK;
/*
This is hackish and a un-laravel way to handle the requirement of \iSDK but unfortunately the xmlrpc3.0 lib doesn't want to correctly encode values when run with a namespace. Will try to resolve this later.
*/
require_once(__DIR__.'/isdk.php');
class Infusionsoft extends \iSDK {
protected $_app;
/**
* Init the sdk
*
*/
public function __construct( $connectionName )
{
$this->_app = parent::cfgCon($connectionName);
}
public function test()
{
dd('works');
}
}
I set up the service provider and the facade alias of Infusionsoft in app/config/config.php.
When I try running methods belonging to the extended iSDK class against an instance of Spoolphiz\Infusionsoft\Facade\Infusionsoft I get undefined method errors, such as the following:
Call to undefined method Spoolphiz\Infusionsoft\Facades\Infusionsoft::loadCon()
Why is this? The whole point of facades is to be able to call methods against its root class...
Looks like I was being stupid. I was developing this package in the laravel workbench. Once it was done I submitted it to packagist and set up a requirement for it in the same laravel app. Having the package installed in vendors directory and in the workbench caused some sort of conflict.
Lesson learned: make sure you dont have the same package in your workbench and in your application's vendors directory.