If I am creating a Facade, and I want to pass parameters before it becomes instantiated, what do I do?
The facade's underlying service is resolved through the IoC container, so all you have to do is bind it correctly.
Create a Service Provider, and pass in whatever you want:
use Illuminate\Support\ServiceProvider;
class FooServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('foo', function()
{
return new Foo('pass whatever you want');
});
}
}
Don't forget to load the service provider in your app's config array.
Then use that bound key in your facade:
class Bar extends Facade {
protected static function getFacadeAccessor() { return 'foo'; }
}
Related
Is possible to call invoke function from a service in symfony?
this is the controller
class FooController extends AbstractController
{
private $fooService;
public function __construct(FooService $fooService)
{
$this->fooService = $fooService;
}
#[Route('/foo', name: 'app_foo')]
public function index(): Response
{
return new Response($this->fooService->__invoke());
//is not possible to do
//return new Response($this->fooService());
}
}
and the service
namespace App\Service;
class FooService
{
public function __invoke()
{
return 'hello';
}
}
I have to call __invoke function explicitly instead to make $this->fooService() is not possible to do it?
In PHP the method call has higher priority than property access so you need to use parentheses.
($this->fooService)()
To access the property and call it.
I'm trying to build a structure that uses dependency injection on lumen.
I Have a Service Layer and repository layer.
I want to inject the repository layer to service layer. Let me try to show you the code
interface IUserRepostitory {
public function getByID($id);
}
class UserRepository extends BaseRepository implements IRepository{
public function getByID($id) {
//Please don't think how this function works, my question about dependency injection
return $this->findOrFail($id);
}
}
interface IService {
public function getByID($id);
}
class UserService implements IService{
private $Repository;
public __construct(IUserRepositor $UserRepository) {
$this->Repository = $UserRepository
}
public function getByID($id) {
return $this->Repository->getByID($id);
}
}
Here I'm registering the dependency resolver.
//Dependency resolver for Repository Layer
class RepositoryServiceProvider extends ServiceProvider {
public function register()
{
$this->app->singleton(IUserRepository::class, function () {
return new UserRepository();
});
}
}
Here I'm registering Service Layer
class ServiceServiceProvider extends ServiceProvider {
public function register()
{
$this->app->singleton(IUserService::class, function () {
//Here is what I don't like
//It would be great a solution that automaticly resolve UserRepository.
return new UserService(new UserRepository());
});
}
}
As you see, I want to auto resolve the dependency into UserService. But singleton method need to create the returning object.
Is there a better way for this?
*** note : please don't pay attention on syntax, I'm writing it on lumen but the same problem on laravel.
Once you bind the UserRepository to the IUserRepository, you may then instantiate the IUserService with the IUserRepository by resolving with the make function!
Modifying your ServiceServiceProvider as such:
class ServiceServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(IUserService::class, function ($app) {
return new UserService($app->make(IUserRepository::class));
});
}
}
I have the following Class:
<?php
namespace App\CustomClasses;
class Disqus {
protected $secretKey;
protected $publicKey;
public function __construct()
{
$this->secretKey = 'abc';
$this->publicKey = '123';
}
public function payload()
{
...
}
}
I have also created a Service Provider (simplified bellow), to bind this class to the IOC container:
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\CustomClasses\Disqus;
class DisqusServiceProvider extends ServiceProvider {
public function register()
{
$this->app->singleton('Disqus', function() {
return new Disqus();
});
}
public function boot()
{
//
}
}
And in my controller:
<?php
use App\CustomClasses\Disqus;
class ArticlesController extends Controller {
public function view(Disqus $disqus)
{
...
//$disqus = App::make('Disqus');
return View::make('articles.view', compact(array('disqus')));
}
}
The problem is that whenever I use the $disqus variable, it is not 'generated' from the Service Provider, but the Disqus class itself.
However, when I have $disqus = App::make('Disqus');, the variable goes through the service provider.
So my question is, since the binding exists in the Service Provider, shouldn't the $disqus variable come from the DisqusServiceProvider rather than the Disqus class directly when I use it in my controller?
Am I missing something?
Thanks in advance for any help.
When the controller's action requires an object of class App\CustomClasses\Disqus to be passed, the service container searches its mappings for the class name of the dependency to see if it has the corresponding service. However, it uses the fully qualified class name and this is the reason it doesn't work correctly in your case.
In your service provider you have bound the service to Disqus, while the fully qualified class name is App\CustomClasses\Disqus. Use the fully qualified class name in the provider and it should work.
I am trying to get my Facade class to work, however it seems Laravel is calling the method on my Facade class instead of calling it on my root class. So I get method undifined error. When I create the the feed class directly from the binding ( App::make('feed')->addArticle();) it works. So I think there is soemthing wrogn with my Facade. Any ideas? Thank in advance.
Controller
class RssController extends BaseController
{
public function getArticles() {
Feed::addArticle();
}
}
ServiceProvider
use Illuminate\Support\ServiceProvider;
class FeedServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('feed', function()
{
return new Feed;
});
}
}
Facade class
use Illuminate\Support\Facades\Facade;
class FeedFacade extends Facade {
protected static function getFacadeAccessor()
{
return 'feed';
}
}
Root Class
class Feed {
//vars
public function __construct()
{
}
public function make() {
return new Feed();
}
public function addArticle() {
return '#addArticle';
}
The problem seems to be you want both your Laravel Facade (Feed::), and the implementation class of your service provider (class Feed) to have the same name. Facades work because in app/config/app.php there's an alias section
'aliases' => array(
'App' => 'Illuminate\Support\Facades\App',
'Artisan' => 'Illuminate\Support\Facades\Artisan',
'Auth' => 'Illuminate\Support\Facades\Auth',
'Blade' => 'Illuminate\Support\Facades\Blade',
This aliasing means whenever you use, say, the App facade
`App::someMethod`
Laravel actually invokes the getFacadeAccessor on Illuminate\Support\Facades\App. There's no global class App in the system. If there were it would cause a similar problem with the facade.
Get an alias for Feed=>Illuminate\Support\Facades\Facade\FeedFacade into your system, and get your implementation class Feed out of the global namespace (moving the file to an appropriate place)
<?php
namespace My\Namespace;
class Feed
{
}
...
return new \My\Namespace\Feed;
and you should be all set.
Also, at the risk of confusing you, you don't need to drop your own classes in the Illuminate\Etc\... namespace, and you should probably put them in your own unless you're trying to get the core Laravel team to accept your classes as the official feed service.
I am having an issue getting a Facade to work properly with a dependency injected into the underlying class.
I have a class called 'Listing'. It has one dependency called 'AdvertRepository' which is an interface and a class called EloquentAdvert which implements the interface. The code for these three classes is here:
// PlaneSaleing\Providers\Listing.php
<?php namespace PlaneSaleing\Providers;
use PlaneSaleing\Repositories\Advert\AdvertRepository;
class Listing {
protected $advert;
public function __construct (AdvertRepository $advert_repository) {
$this->advert = $advert_repository;
}
public function test() {
$this->advert->test();
}
public function test2() {
echo "this has worked";
}
}
// PlaneSaleing\Repositories\Advert\AdvertRepository.php
<?php namespace PlaneSaleing\Repositories\Advert;
interface AdvertRepository {
public function test();
}
// PlaneSaleing\Repositories\Advert\EloquentAdvert.php;
<?php namespace PlaneSaleing\Repositories\Advert;
class EloquentAdvert implements AdvertRepository {
public function test() {
echo 'this has worked';
}
}
I have then created a service provider called ListingServiceProvider.php, which has the following code:
// PlaneSaleing/Providers/ListingServiceProvider.php
<?php namespace PlaneSaleing\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\App;
class ListingServiceProvider extends ServiceProvider {
public function register() {
App::bind('PlaneSaleing\Repositories\Advert\AdvertRepository', 'PlaneSaleing\Repositories\Advert\EloquentAdvert');
}
}
I also added this to the ServiceProviders array in app.php
Now, if I inject Listing as a dependency into a controller and call the test method (as shown below) Laravel correctly detects the dependency, instantiates EloquentAdvert via its binding and displays 'this has worked'.
// Controllers/TestController.php
use PlaneSaleing\Providers\Listing;
class TestController extends BaseController {
protected $listing;
public function __construct(Listing $listing) {
$this->listing = $listing;
}
public function test1() {
$this->listing->test();
}
}
Now, I then created a facade for Listing. I added a new facade as follows and added an alias in app.php:
// PlaneSaleing\Providers\ListingFacade.php
<?php namespace PlaneSaleing\Providers;
use Illuminate\Support\Facades\Facade;
class ListingFacade extends Facade {
protected static function getFacadeAccessor() {
return 'Listing';
}
}
I also added the following new lines to ListingServiceProvider.php:
<?php namespace PlaneSaleing\Providers;
use PlaneSaleing\Repositories\Advert\AdvertRepository;
use PlaneSaleing\Repositories\Advert\EloquentAdvert;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\App;
class ListingServiceProvider extends ServiceProvider {
public function register() {
App::bind('PlaneSaleing\Repositories\Advert\AdvertRepository', 'PlaneSaleing\Repositories\Advert\EloquentAdvert');
// New lines...
$this->app['Listing'] = $this->app->share(function() {
return new Listing(new AdvertRepository);
});
}
}
NOW...if I call Listing::test(), I get the following error: Cannot instantiate interface PlaneSaleing\Repositories\Advert\AdvertRepository.
If I call Listing::test2() , I get 'this has worked' so it seems the Facade is working correctly.
It seems that when accessing Listing via its Facade the binding between AdvertRepository and EloquentAdvert doesnt work. I have looked at my code in the ServiceProvider thinking it was the issue, but I cant figure it out.
Both the Facade and binding work when tested individually but not when both are used at the same time.
Any ideas???
OK, So I have figured it out...For those who run into a similar problem...
The offending statement was in ListingServiceProvider.php which read:
$this->app['Listing'] = $this->app->share(function() {
return new Listing(new AdvertRepository);
});
The error is the new AdvertRepository statement. The reason being is that, we are telling php to directly instantiate the interface 'AdvertRepository'. Instead, we need to tell Laravel to instantiate the appropriate implementation of the 'AdvertRepository' interface. To do that, we use App::make('AdvertRepository'). That way, Laravel uses the binding previously declared to instantiate the correct implementation.
If your constructor is not being inject with a class, you must tell Laravel what class will be used when it needs to instantiate a particular interface:
Put this in your filters or bindings file:
App::bind('PlaneSaleing\Repositories\Advert\AdvertRepository', function()
{
return new PlaneSaleing\Repositories\Advert\EloquentAdvert;
});