Dependency Injection of Interface - php

Hi I'm getting an error of should be an instance of interface
App\Repositories\Traits\PhotoService::__construct() must be an instance of AwsServiceInterface, instance
Here's what I have so far
namespace App\Repositories\Interfaces;
interface AwsServiceInterface
{
//...
}
Now I have this class
namespace App\Repositories\Interfaces;
use App\Repositories\Interfaces\AwsServiceInterface;
class CloudFrontService implements AwsServiceInterface
{
public function __construct()
{
}
}
Now I'm using a dependency injection on this class
namespace App\Repositories\Traits;
use App\Repositories\Interfaces\AwsServiceInterface;
class PhotoService
{
protected $service;
public function __construct(AwsServiceInterface $service)
{
$this->service = $service;
}
public function getAuthParams($resource, $search_key = '')
{
// Execute a function from a class that implements AwsServiceInterface
}
And I'm calling the PhotoService class like this
$photo_service = new PhotoService(new CloudFrontService());
echo $photo_service->getAuthParams($resource);
But somehow I'm getting this error
FatalThrowableError: Type error: Argument 1 passed to App\Repositories\Traits\PhotoService::__construct() must be an instance of AwsServiceInterface, instance of App\Repositories\Interfaces\CloudFrontService given

In your App\Providers\AppServiceProvider class add following code in register() method:
$this->app->bind(
'App\Repositories\Interfaces\AwsServiceInterface',
'App\Repositories\Interfaces\CloudFrontService'
);
and then you can use it as:
$photo_service = app(PhotoService::class);
echo $photo_service->getAuthParams($resource);

You are having a problem with namespacing. The typehint that you are using isn't complete for what you are looking for.
Just guessing but I think that you want to change the typehint to be:
public function __construct(App\Repositories\Interfaces\AwsServiceInterface $service)
http://php.net/manual/en/language.namespaces.basics.php

You need to bind implementation to that interface. Here's an example.
Laravel itself does not know what implementation you want to use for this interface, you need to specify that yourself.

I solved my problem. For those of you having the same issue make sure you do this step.
1. as mentioned by #Amit double check on the service provider that the service is bind under register function
2. Make sure to do
php artisan clear-compile
php artisan cache:clear
php artisan config:clear

Related

I can't inject two or more class on my service provider

I am currently implementing repository pattern and dependency injection on my laravel project. But when I inject two or more class, I got this error
Too few arguments to function App\Repositories\UserRepository::__construct(), 0 passed
I don't know what's wrong but I think I did it correctly. Here's my code:
My service provider:
public function register()
{
$this->app->bind(LoanRepository::class, function() {
return new LoanRepository(new Loan, new UserRepository);
});
}
and here's my Repository
public function __construct($loan, $userRepository)
{
$this->loan = $loan;
$this->userRepository = $userRepository;
}
does anyone experience this?
The error is telling you that the UserRepository class is expecting some arguments that you are not passing it. Below would be an example of how you would pass a constructor argument to your new UserRepositiry call.
public function register()
{
$this->app->bind(LoanRepository::class, function() {
return new LoanRepository(new Loan, new UserRepository($constructorArgsHere);
});
}
I haven't worked with Laravel so I don't know what the UserRepository class constructor is looking for as arguments, but that should be an easy thing to find out just looking at the file where that class is defined or a quick google search.

Adding Facades before namespace in Laravel , How it works?

Okay there are questions about the same topic before but they don't help to fully understand this topic
SO SuggestionFirst
SO Suggestion Second
All the code is just to illustrate the situation, So this is the structure
A helper function which does something
namespace App\Helpers;
class Pets{
public function limit($string,$limit,$start = 0){
return substr($string,$start,$limit);
}
}
Now in order to use this helper, since it's a class so i need to create an object like this
CODE SAMPLE FIRST
namespace App\Objects;
use App\Helpers\Pets;
class User{
public function getShortUserName(){
$name = auth()->user()->first_name.' '.auth()->user()->last_name;
$pet = new Pets;
return $pet->limit($name,10);
}
}
But somewhere I got to know that if you add Facades before your namespace, you can call the function statically even if they are non static function like this
CODE SAMPLE SECOND
namespace App\Objects;
use Facades\App\Helpers\Pets;
class User{
public function getShortUserName(){
$name = auth()->user()->first_name.' '.auth()->user()->last_name;
return Pets::limit($name,10);
}
}
Now what I want to know is I have 2 sample codes with namespace as follows
use App\Helpers\Pets;
use Facades\App\Helpers\Pets;
By adding the Facades I can call the function statically but how, that's not a valida namespace in my app
What laravel doing behind the scene, I am so confused
Thank you for your time ;)
What you are describing is Laravels Real-Time Facades.
You can find documentation of the functionality here:
https://laravel.com/docs/6.x/facades#real-time-facades
I will not enter too much in details but this is a simple explanation of what's behind the scenes when you use facades in laravel.
Let's suppose you define a custom class with some public methods:
namespace Test;
class Foo
{
public function test()
{
return 'test';
}
}
Then you have to define a facade for this class:
namespace Test1;
class BarFacade
{
// In laravel this is called in the Facade abstract class but it is actually implemented
// by all the facades you add across the application
public static function getFacadeAccessor()
{
// In laravel you can also return a string which means that the object
// will be retrieved from the container.
return new \Test\Foo();
}
// In laravel this method is defined in the Facade abstract class
public static function __callStatic($method, $args)
{
$object = self::getFacadeAccessor();
return call_user_func_array([$object, $method], $args);
}
}
Then, you have to define the alias in the $aliases array of the config.app file. These aliases are parsed by laravel and registered using the php built-in function class_alias (see Illuminate/Foundation/AliasLoader.php)
class_alias('Test\Foo', 'BarFacade', true);
// You can also create an alias for the facade itself
class_alias('Test1\BarFacade', 'FooBar', true);
Then you can simply call the facades:\
var_dump(BarFacade::test());
var_dump(\Test1\BarFacade::test());
var_dump(\FooBar::test());
The results would obviously be:
string(4) "test"
string(4) "test"
string(4) "test"

Laravel Target is not instantiable while building

I created an Artisan command which worked and where I injected a Kafka client service as the first parameter and a concrete class BudgetsTransformer, as the second parameter.
class ConsumeBudgetsCommand extends Command {
public function __construct(FKafka $kafkaClient, BudgetsTransformer $transformer)
{
$this->kafkaClient = $kafkaClient;
$this->transformer = $transformer;
parent::__construct();
}
}
AppServiceProvider class looked like:
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('kafka.client', function ($app) {
return new \Weq\FKafka\FKafka();
});
$this->app->bind('budget.transformer', function ($app) {
return new BudgetsTransformer();
});
}
public function boot()
{
$this->app->bind('consume:budgets', function ($app) {
return new ConsumeBudgetsCommand($app['kafka.client'], $app['budget.transformer']);
});
$this->commands('consume:budgets');
}
}
So far all is working properly. Then I decided to create a TransformerInterface which BudgedTransformer implements (and other future Transformers will implement it):
class BudgetsTransformer implements TransformerInterface
{
// ...
}
and I changed the signature in the command to inject the interface instead of the concrete class:
class ConsumeBudgetsCommand extends Command {
public function __construct(FKafka $kafkaClient, TransformerInterface $transformer)
{
$this->kafkaClient = $kafkaClient;
$this->transformer = $transformer;
parent::__construct();
}
}
But I get the following issue when I try to run some artisan command
In Container.php line 933:
Target [App\Transformers\TransformerInterface] is not instantiable while building [App\Console\Commands\ConsumeBudgetsCommand].
I run previously the issue the following artisan command just in case. cache:clear, clear-compiled, optimize and so on but no luck.
What I'm doing wrong? Should I bind the BudgetTransformer in a different way I'm doing now for passing and Interface instead of a concrete class?
I added:
$this->app->bind(TransformerInterface::class, BudgetsTransformer::class);
in AppServiceProvider::register() and I removed
$this->app->bind('budget.transformer', function ($app) {
return new BudgetsTransformer();
});
there, then I update in AppServiceProvider::boot() the command binding:
$this->app->bind('consume:budgets', function ($app) {
return new ConsumeBudgetsCommand($app['kafka.client'], $app[TransformerInterface::class]);
});
But still not working, anyway this approach (even working) will not resolve the issue since when I want to add another different transformer implementation, let's say CostTransformer which implements TransformerInterface is gonna always inject BudgetTransformer. So reading the documentation in the link, I found that Contextual Binding could be the solution, so I substituted by:
$this->app
->when(ConsumeBudgetsCommand::class)
->needs(TransformerInterface::class)
->give(function ($app) {
return new BudgetsTransformer();
});
So in that way, I will be able to inject different implementations of transformers to different commands by injecting the interface. But still not working.
Can someone tell me how exactly declare the command binding
$this->app->bind('consume:budgets', function ($app) {
return new ConsumeBudgetsCommand($app['kafka.client'], ???);
});
to use that Contextual binding?
For binding interfaces must be use this structure https://laravel.com/docs/5.5/container#binding-interfaces-to-implementations
$this->app->bind(TransformerInterface::class, BudgetsTransformer::class);
And
$this->app->bind('consume:budgets', function ($app) {
return new ConsumeBudgetsCommand($app['kafka.client'], $app->make(TransformerInterface::class));
});

Behavior-Driven-Development is failing my expectations with PHPSpec

I have the following class generated through PHPSpec:
class Consumer
{
public function __construct($accesskey, $accessToken)
{
// TODO: write logic here
}
}
When I test the constructor I get an error that it is missing Argument 1. Below is how I have written the behavior:
namespace spec\Zizy\Aggregator\Context;
use Zizy\Aggregator\Context\Contract\ContextContractInterface;
use Zizy\Aggregator\Context\Consumer;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ConsumerSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->beConstructedWith( md5('samplekey'), md5('sampletoken') );
$this->shouldHaveType(Consumer::class);
}
/**
* This spec describes how we would access our consumer directry
*/
public function it_gets_access_token()
{
$this->getAccessToken()->shouldReturn(md5('sampletoken'));
}
}
Below is the error I get when running PHPSpec.
Zizy\Aggregator\Context\Consumer 21 - it gets access token
warning: Missing argument 1 for Zizy\Aggregator\Context\Consumer::__construct() in C:\wamp64\www\spikes\src\Context\Consumer.php line 7
I have also tried to test my consumer through an interface but PHPSpec keeps telling me that it cannot find the interface but in a class context thus offer me an opportunity to create the class meanwhile it should actually be an interface.
How can I also write code through interfaces with PHPSpec?
You'll need to specify the constructor arguments for every example case. If you find that a bit too laborious, you can use let to make preparations for before each example is run. For your case, something like this should work:
namespace spec\Zizy\Aggregator\Context;
use Zizy\Aggregator\Context\Contract\ContextContractInterface;
use Zizy\Aggregator\Context\Consumer;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ConsumerSpec extends ObjectBehavior
{
function let()
{
$this->beConstructedWith( md5('samplekey'), md5('sampletoken') );
}
function it_is_initializable()
{
$this->shouldHaveType(Consumer::class);
}
/**
* This spec describes how we would access our consumer directry
*/
public function it_gets_access_token()
{
$this->getAccessToken()->shouldReturn(md5('sampletoken'));
}
}

Laravel 4 setting up model using the IoC container

I recently watched this video and wanted to change my Laravel controllers so that they had their dependencies managed with Laravel's IoC container. The video talks about creating an interface for a Model and then implementing that interface for the specific data source used.
My question is: when implementing the interface with a class that extends Eloquent and binding that class to the controller so that it is accessible from $this->model, should I also create interfaces and implementations for the Eloquent models which may be returned when calling methods such as $this->model->find($id)? Should there be different classes for the Model and the ModelRepository?
Put it another way: how do I do new Model when my model is in $this->model.
Generally, yes, people doing that pattern (the repository pattern) have an interface which have some methods defined that your app will use:
interface SomethingInterface {
public function find($id);
public function all();
public function paged($offset, $limit);
}
Then you create an implementation of this. If you're using Eloquent, then you can make an Eloquent implementation
use Illuminate\Database\Model;
class EloquentSomething {
protected $something;
public function __construct(Model $something)
{
$this->something = $something;
}
public function find($id)
{
return $this->something->find($id);
}
public function all() { ... }
public function paged($offset, $limit) { ... }
}
Then you make a service provider to put it all together, and add it into app/config/app.php.
use Something; // Eloquent Model
use Namespace\Path\To\EloquentSomething;
use Illuminate\Support\ServiceProvider;
class RepoServiceProvider extends ServiceProvider {
public function register()
{
$app = $this->app;
$app->bind('Namespace/Path/To/SomethingInterface', function()
{
return new EloquentSomething( new Something );
});
}
}
Finally, your controller can use that interface as a type hint:
use Namespace/Path/To/SomethingInterface;
class SomethingController extends BaseController {
protected $something;
public function __construct(SomethingInterface $something)
{
$this->something = $something;
}
public function home() { return $this->something->paged(0, 10); }
}
That should be it. Apologies on any errors, this isn't tested, but is something I do a lot.
Downsides:
More code :D
Upsides:
Able to switch out implementations (instead of EloquentSomething, can use ArraySomething, MongoSomething, whatever), without changing your controller code or any code that uses an implementation of your interface.
Testable - you can mock your Eloquent class and test the repository, or mock your constructor dependency and test your controller
Re-usable - you can App::make() to get the concrete EloquentSomething anywhere in your app and re-use the Something repository anywhere in your code
Repository is a good place to add additional logic, like a layer of cacheing, or even validation rules. Stock mucking about in your controllers.
Finally:, since I likely typed all that out and STILL DIDN'T ANSWER YOUR QUESTION (wtf?!), you can get a new instance of the model using $this->model. Here's an example for creating a new Something:
// Interface:
public function create(array $data);
// EloquentSomething:
public function create(array $data)
{
$something = this->something->newInstance();
// Continue on with creation logic
}
Key is this method, newInstance().
I've used $newModel = $this->model and it's worked for me.

Categories