I'm trying to deepen my knowlade in laravel architecture.
I have a search engine (elastic search for the sake of the example), but this search engine might change in the future. So im trying to write a container for this, so in case i'll change the engine in the future, i will have to change only the container. (I believe the termenology is factory design?)
I have created a provider app/providers/DataFromSearchEngine.php that looks like this:
use Illuminate\Support\ServiceProvider;
class DataFromSearchEngine extends ServiceProvider {
public function boot()
{
//
}
public function register()
{
$this->app->singleton('SearchEngine', function($app) {
return new elasticSearch;
});
}
}
Then i registered it in the providers array in config/app.php .
'providers' => [
// providers...
'App\Providers\DataFromSearchEngine'
],
The next step is to call SearchEngine from my controller:
namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class SearchController extends Controller {
protected $searchEngine;
public function __construct() {
$this->searchEngine = $this->app->make('SearchEngine');
}
}
But all these yields: Undefined property: App\Http\Controllers\SearchController::$app
Can someone explain what i'm missing?
Instead of using $this->app try using app().
This is because non of the inherited controller classes, i.e. App\Http\Controllers\Controller or Illuminate\Routing\Controllers\Controller have an app property on them.
As a note you can use app('SearchEngine') which is the equivalent of app()->make('SearchEngine') as a shortcut to making your object.
I had this issue when trying to create a service provider. I registered my service provider in AppServiceProvider.php but was still getting this same error. The issue was that in my ServiceProvider I needed to add extends ServiceProvider to my class. Seems simple but is often forgotten.
Related
I am trying to inject a Manager class into toe Service Container of Lumen. My goal is to have a single instance of LogManager which is available in the whole application via app(LogManager::class).
Everytime i try to access this shortcut i get the following exeption:
[2017-03-23 16:42:51] lumen.ERROR: ReflectionException: Class LogManager does not
exist in /vendor/illuminate/container/Container.php:681
LogManager.php (i placed that class in the same location where my models are (app/LogManager.php))
<?php
namespace App;
use App\LogEntry;
class LogManager
{
...
}
AppServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\LogManager;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->singleton(LogManager::class, function ($app) {
return new LogManager();
});
}
}
I uncommented the line $app->register(App\Providers\AppServiceProvider::class); in bootstrap/app.php
I think that i missed something with the correct namespacing or placement of the classes espaccially LogManager. Maybe some one is willing to give me a hint?
If you need some more informations just give me a hint!
Your class and your service provider look fine. However, wherever you're calling app(LogManager::class) also needs to know the fully qualified name of the class.
Either make sure you have use App\LogManager at the top of the file, or change your call to app(\App\LogManager::class).
Thanks to all in advance.
I am trying to create Facades for my custom and common functions in laravel 5.0 also I don`t want to create controller for that so I am using Facades.
I have tried almost every tutorial but it do not help me.
Please help me to create facade without using Composer in Laravel 5.0.
Thanks again.
First of all you're creating a class of facade like this:
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class SomeFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'someService';
}
}
then You create a service class that hold your functionalities:
namespace App\Services;
class SomeService { ... }
Finally you have to register it and set an alias (not required) for it:
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProivider extends ServiceProvider
{
(...)
public function register()
{
$this->app->singleton('someService', function () {
return new \App\Services\SomeService();
});
$this->app->alias('SomeServiceFacade', \App\Facades\SomeFacade::class);
}
}
Now you can call your methods from SomeService with:
SomeServiceFacade::someMethhod();
or
app('someService')->someMethhod();
I have a service provider that I want to use to bind an instance of a class to the service container:
namespace App\Providers;
use Eluceo\iCal\Component\Calendar;
use Illuminate\Support\ServiceProvider;
class IcalProvider extends ServiceProvider
{
public function register()
{
$this->app->instance('iCal', function () {
return new Calendar(config('calendar.name'));
});
}
}
As I understand the documentation on binding an instance, this allows me to bind the key iCal to the service container so that later in my controller or service class I can type hint iCal and the instance created in the service provider will be used.
So I created a controller and tried to type hint my instance:
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
class CalendarInviteController extends Controller
{
public function download(iCal $ical, $sessionId)
{
dd($ical);
}
}
But when I do I get the error:
Class App\Http\Controllers\iCal does not exist
Makes sense, as it applies it's looking for a class named iCal in the controller namespace which doesn't exist. There's not a use statement for the instance since iCal is just a text key, so I tried telling it to look at the root namespace thinking that may fix it:
public function download(\iCal $ical, $sessionId)
and I get the error:
Class iCal does not exist
When I read the section of the documentation on resolving from the service container it looks like the only thing I need to do in the controller is type hint to get the instance.
Am I misunderstanding the docs?
Update
I should also mention that I did add my service provider to my config/app.php file.
Also, when I create an interface, bind it to the service container instead, edit the vendor code to implement said interface, and inject that interface instead it works, but that requires that I edit the vendor code which I don't want.
As you see in the docs the method instance takes a key and an object instance to register in the container. So, if you want to register a specific instance in the container, the registration should be:
namespace App\Providers;
use Eluceo\iCal\Component\Calendar;
use Illuminate\Support\ServiceProvider;
class IcalProvider extends ServiceProvider
{
public function register()
{
//register a specific instance of the Calendar class in the container
$this->app->instance('iCal', new Calendar(config('calendar.name') );
}
}
This way you could get back the instance with:
$cal = \App::make('iCal');
If your purpose is to type-hint the class in the controller method, and you want to resolve the previous registered instance from the service container, you could do like this:
namespace App\Providers;
use Eluceo\iCal\Component\Calendar;
use Illuminate\Support\ServiceProvider;
class IcalProvider extends ServiceProvider
{
public function register()
{
//the key will be 'Eluceo\iCal\Component\Calendar'
$this->app->instance( Calendar::class, new Calendar(config('calendar.name') );
}
}
Now, in your controller:
namespace App\Http\Controllers;
//important: specify the Calendar namespace
use Eluceo\iCal\Component\Calendar;
class CalendarInviteController extends Controller
{
public function download(Calendar $ical, $sessionId)
{
dd($ical);
}
}
This way Laravel will see that you want a Calendar object and it will try to get it from the service container looking if exists a binding for this key: (because this is the namespace of the class you have specified in the controller)
Eluceo\iCal\Component\Calendar
and the binding exists! As you have bound this key to your service container in your service provider, so Laravel will return your registered instance.
In the code you provided, you tipe-hinted the class iCal, but the class didn't exist anywhere so Laravel wasn't able to instantiate the class
If you’re wanting to inject dependencies into your controller (which is good, so kudos!) then you need an interface name to type-hint on.
Usually you would have a generic interface, and then bind that interface to a concrete implementation. So you may have a calendar service interface, that’s bound to your iCal implementation. Something like this:
use Eluceo\iCal\Component\Calendar;
class CalendarServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('App\Services\Calendar', function ($app) {
return new Calendar(config('calendar.name'));
});
}
public function provides()
{
return ['App\Services\Calendar'];
}
}
So long as you register your service provider in your config/app.php file, you can now type-hint your calendar dependency in classes:
use App\Services\Calendar;
class InvitationController extends Controller
{
protected $calendar;
public function __construct(Calendar $calendar)
{
$this->calendar = $calendar;
}
}
I've created a ServiceProvider in Laravel5 that registers two singletons to the service container:
ServiceProvider
namespace App\Providers;
use App\Services\Passwords\FileMakerPasswordBroker;
use App\Services\Passwords\FileMakerTokenRepository;
use Illuminate\Support\ServiceProvider;
class FileMakerPasswordResetServiceProvider extends ServiceProvider
{
protected $defer = true;
public function provides(){
return ['fm.password','fm.password.token'];
}
public function register()
{
$this->registerPasswordBroker();
$this->registerTokenRepository();
}
protected function registerPasswordBroker(){
$this->app->singleton('fm.password', function ($app){
return new FileMakerPasswordBroker;
});
}
protected function registerTokenRepository(){
$this->app->singleton('fm.password.token', function ($app){
return new FileMakerTokenRepository;
});
}
}
I've just started so the two concrete classes being created are just empty class declarations:
TokenRepository
namespace App\Services\Passwords;
class FileMakerTokenRepository {
}
PasswordBroker
namespace App\Services\Passwords;
class FileMakerPasswordBroker {
}
And I've registered my service provider in my config/app.php class:
app.php
...
'providers' => [
...
App\Providers\FileMakerPasswordResetServiceProvider::class
...
The problem is, I can't resolve the singletons out of the service container:
A dev controller
...
use Illuminate\Foundation\Application;
class DevController extends Controller
{
protected $app;
public function __construct(Application $app){
$this->app = $app;
}
public function testPasswordReset(){
// This throws an error
return $this->app->make('fm.password.token');
}
...
When I try to make the singleton, I get the error
ReflectionException in Container.php line 736:
Class fm.password.token does not exist
I walked back through the documentation on binding and resolving from the service container and it looks like everything is right, but I'm obviously missing something.
Is there a step I'm missing or something I'm missing re: resolving a singleton from the service container?
Update
Here's a screen shot of the stack if that helps:
Another interesting fact: When I dump the application to the browser from the dev controller:
dd($this->app);
And look at the list of service providers, the FileMakerPasswordResetServiceProvider class is not present:
The provider is eventually going to be deferred, but at the moment I have the deferred property commented out, so (as far as I know) it should be getting loaded. I see other providers that I've registered in the stack. This may be another clue.
I have a simple setup where I need to show a bit of data universally across my app, in the header of my site. To do so, I have created a ComposerServiceProvider class and a HeaderComposer class to delegate this responsibility, as seen below:
ComposerServiceProvider
use Illuminate\Support\ServiceProvider;
class ComposerServiceProvider extends ServiceProvider {
public function register() {
$this->app->view->composer('templates.header', 'HeaderComposer');
}
}
HeaderComposer
class HeaderComposer {
public function compose($view) {
Event::listen('illuminate.query', function($q) {
print_r($q);
});
$view->with('nearbyMissions', array(
'past' => Mission::remember(60, 'previousMissions')->previousMissions(3)->get(),
'future' => Mission::remember(60, 'nextMissions')->nextMissions(3)->get()
));
}
}
Previously, these classes were not namespaced, but I have now decided to namespace each of them as a good practice, by prepending the files with:
namespace MyProject\Composers;
However, this has broken my application as some part of my project can no longer resolve my composer classes. None of my pages work because they all use a templated header view which uses my HeaderComposer:
Class HeaderComposer does not exist (View: H:\myproject\app\views\templates\main.blade.php)
Where am I meant to declare the use statements for my class? In my view? (Which doesn't seem right). Somewhere else?
Since you just namespaced your class, then you have to update your composer binding:
use Illuminate\Support\ServiceProvider;
class ComposerServiceProvider extends ServiceProvider {
public function register() {
$this->app->view->composer('templates.header', 'MyProject\Composers\HeaderComposer');
}
}