Injection in Repository - laravel

I found many questions about my problem and tried (I think) all the solutions, but I can not make it work. I'm overlooking something very easy, probably.
I'm using Laravel 5. I try to implement a repository-pattern.
I have an Eloquent model '\Notos\Kind'.
Then I have this interface:
namespace Notos\Contracts;
interface Kind
{
public function findByName($name);
}
My Repository looks like this:
namespace Notos\Repository;
use Illuminate\Database\Eloquent\Model;
use Notos\Contracts\Kind as KindContract;
class Kind implements KindContract
{
protected $kind;
public function __construct(Model $kind)
{
$this->kind = $kind;
}
public function findByName($name)
{
return $this->kind->firstOrCreate(array('name' => $name));
}
}
My ServiceProvider:
namespace Notos\Providers;
use Illuminate\Support\ServiceProvider;
use Notos\Kind;
use Notos\Repository\Kind as KindRepository;
class RepoServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app->bind('Notos\Contracts\Kind', function ($app) {
return new KindRepository(new Kind);
});
}
}
And my controller that uses the repository:
namespace Notos\Http\Controllers;
use Notos\Repository\Kind as KindRepository;
class KindController extends Controller
{
protected $kind;
public function __construct(KindRepository $kind)
{
$this->kind = $kind;
}
public function find($name)
{
return $this->kind->findByName($name);
}
}
This provider is registered in config/app.php
When I try to execute the KindController#find I get this error:
BindingResolutionException in Container.php line 785:
Target [Illuminate\Database\Eloquent\Model] is not instantiable.
I can't find what I'm doing wrong.
It works perfect if I change __construct(Model $kind) to __construct(Kind $kind).
Any ideas?
Thanks

The first thing, I would advice you to add to your class names the function so for example instead of Kind for repository use KindRepository, the same for contract. Otherwise you will have 3 kinds classes (of course in different namespaces) but it will be hard to analyse the code.
The problem here is that in your KindController you try to inject KindRepository directly but in constructor you use Model. It won't work because Model is only abstract class. What you should do to make it work?
In the code:
$this->app->bind('Notos\Contracts\Kind', function ($app) {
return new KindRepository(new Kind);
});
you told Laravel that when you will use Notos\Contracts\Kind it should create KindRepository for you with Kind in constructor - look you told here about Contract, not about repository itself.
So to make it work, you should use in your controller not your repository, but your contract.
Instead of line:
use Notos\Repository\Kind as KindRepository;
you should write:
use Notos\Contracts\Kind as KindRepository;
and it should work now.

Related

Laravel Package: How to register a third party facade inside a custom package?

I'm creating a package that uses internally this hashid package.
How can I register a third party facade inside a custom package?
I tried three options and none of them worked.
Version - Composer
"aliases": {
"Hashids": "Vinkla\\Hashids\\Facades\\Hashids"
}
Version - inside my ServiceProvider with alias
class MyPackageServiceProvider extends ServiceProvider
{
public function register()
{
...
$this->app->alias(\Vinkla\Hashids\Facades\Hashids::class, 'Hashids');
}
Version - inside my ServiceProvider with AliasLoader
class MyPackageServiceProvider extends ServiceProvider
{
public function register()
{
...
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
$loader->alias('Hashids', \Vinkla\Hashids\Facades\Hashids::class);
}
When I'm testing the code, I get the error:
Error: Call to undefined method Vinkla\Hashids\Facades\Hashids::encode()
inside
/** #test */
public something_to_test()
{
dd(\Hashids::encode(1));
}
Ok I found one solution, but I am still confused why it is like that.
In my "MyPackageServiceProvider" I need to add:
$this->app->register(HashidsServiceProvider::class);
Why do I need to register a Provider? I thought the composer is handling the work.
At the end only this works:
$this->app->register(HashidsServiceProvider::class);
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
$loader->alias('Hashids', \Vinkla\Hashids\Facades\Hashids::class);
The other versions doesn't work as well :(

Facades vs. Classes with static methods in Laravel

I was looking around the Laravel framework and some of their products and I noticed that Cashier is using the Casheir class with static methods compared to Socialite, which is used as a facade.
What are the benefits/downsides of building it one or the other way, or is there none at all?
I would like to build something myself, but I don't want to start building it as a class with static methods if building it as a facade is a better solution.
When you may need multiple implementations, a single interface can be defined through facade to simplify the code
Building it as a class with static methods:
When you have multiple classes you have to do something like this:
CashierOne::method, CashierTwo::method ....
Used as a facade:
According to what you bind to the container to switch the implementation
You only need to call through an interface:
// Define a Cashier Facade
class Cashier extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'cashier';
}
}
// In CashServiceProvider
$this->app->singleton('cashier', function ($app) {
return new CashierManager ($app);
});
// In CashierManager
public function gateway($name = null)
{
// get cashier implementation by name
}
public function __call($method, $parameters)
{
return $this->gateway()->$method(...$parameters);
}
// In Controller
Cashier::method
In addition, the facade is easier to test, check:
https://laravel.com/docs/5.8/facades#how-facades-work

How to override a single function in a package for internal use with laravel?

I would like to override a function in vendor/laravel/framework/src/Illuminate/View/Compilers/BladeCompiler.php
or to be precise a function iside the trait Illuminate\View\Compilers\Concerns\CompilesEchos.php used by that file. However I could not find very clear information on how to override packages. Could someone please help me understand how.
I understand that I need to extend BladeCompiler
Let's call it MyBladeCompiler
class MyBladeCompiler extends BladeCompiler
{
public function compileEchoDefaults($value)
{
return 'test';
return preg_replace('/^(?=\$)(.+?)(?:\s+or\s+)(.+?)$/si', 'isset($1) ? $1 : $2', $value);
}
}
I now would like to register it as the new class to be used. I do understand this should be done in a serviceprovider, but how?
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
$this->app->bind(BladeCompiler::class, MyBladeCompiler ::class);
}
}
this does not work
Create a new service provider called ViewServiceProvider, then in it, delete the register and boot methods, and make it extend Illuminate\View\ViewServiceProvider.
Then, add this method:
public function registerBladeEngine($resolver)
{
// The Compiler engine requires an instance of the CompilerInterface, which in
// this case will be the Blade compiler, so we'll first create the compiler
// instance to pass into the engine so it can compile the views properly.
$this->app->singleton('blade.compiler', function () {
return new MyBladeCompiler(
$this->app['files'], $this->app['config']['view.compiled']
);
});
$resolver->register('blade', function () {
return new CompilerEngine($this->app['blade.compiler']);
});
}
Note how in the singleton method, I'm using your blade compiler class.
Then, open config/app.php, and replace the \Illuminate\View\BladeServiceProvider::class, record, with your own service provider.
So the service provider should look like this:
namespace App\Providers;
use MyBladeCompiler
use Illuminate\View\ViewServiceProvider as BaseViewServiceProvider;
class ViewServiceProvider extends BaseViewServiceProvider
{
public function registerBladeEngine($resolver)
{
$this->app->singleton('blade.compiler', function () {
return new MyBladeCompiler(
$this->app['files'], $this->app['config']['view.compiled']
);
});
$resolver->register('blade', function () {
return new CompilerEngine($this->app['blade.compiler']);
});
}
}
This works by extending the Illuminate view service provider, so all existing methods work as expected. Then, you need to override the registerBladeEngine() method so that your overridden one is called, and not the one in the illuminate provider.
In your overridden method, you're specifying that your compiler should be used instead of the original one.
You then specify that your extended view service provider is used instead of the illuminate one by editing the app.php config file.

Why won't this Laravel 5.4 service provider register?

I am trying to do a hello world service provider with the new Laravel 5.4.
I have created the following service provider file:
//File: app/TestProvider/TestServiceProvider.php
namespace App\TestProvider;
use Illuminate\Support\ServiceProvider;
class TestServiceProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
$this->app->bind('Test', function ($app) {
return new Test();
});
}
}
I have created a simple class under the same namespace:
//File: app/TestProvider/Test.php
namespace App\TestProvider;
class Test
{
/**
* Register bindings in the container.
*
* #return void
*/
public function helloWorld()
{
echo "hello world";
}
}
The problem is, this is not registering. The register method is executing as when I put a breaker before the 'bind' method, it executes:
public function register()
{
dd("BREAKER");
$this->app->bind('Test', function ($app) {
return new Test();
});
}
So this outputs "BREAKER" as expected. However if I put the breaker in the closure, nothing happens which suggests for some reason, that 'bind' method isn't being executed??
Any ideas?
EDIT:
Just some further info: I know that the Test class is registered and in the correct namespace as I can do:
dd(new Test());
in the registration method, and it outputs the resource id as expected.
Explanation
The closure provided only runs when the binding is being resolved. That's why it's a closure, it can be saved in the service container and resolved at any time while the program runs.
Solution
To see the resolved binding, create a controller and resolve the class in that controller:
// File: app/Http/Controllers/TestController.php
namespace App\Http\Controllers;
// This isn't the best way, but it works. See the best way below
class TestController extends Controller {
public function index()
{
return \App::make('Test')->helloWorld();
}
}
Of course, don't forget to register the route:
// File: routes/web.php
Route::get('/', 'TestController#index');
The binding will resolve when you hit the homepage.
However, as I said, it's not the best way, so here I prepared a better way. Change the way you register the binding:
// File: app/Providers/TestProvider.php
namespace App\TestProvider;
use Illuminate\Support\ServiceProvider;
use App\TestProvider\Test;
// Better way
class TestServiceProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
// Note: we bind the exact complete class name!
$this->app->bind(Test::class, function ($app) {
return new Test();
});
}
}
After this change the controller so that it looks like this:
namespace App\Http\Controllers;
use App\TestProvider\Test;
class TestController extends Controller {
/**
* #var Test $test
*/
private $test;
// Let Laravel resolve the dependency on constructing the class
public function __construct(Test $test)
{
$this->test = $test;
}
public function index()
{
return $this->test->helloWorld();
}
}
You will see that the exact same thing happens, but it looks more elegant and avoids conflicts.
Details
Laravel gives only a high level overview of the service container, which doesn't help to learn how it works on the inside. The best way to see that is to go down the call stack.
When you do that, you find that Laravel registers every class in the project in the service container. That means that whether you create a service provider or not, the class will be in the container. How exactly?
When you run php artisan optimize, Laravel creates files that have array with all the classes of the project. When you run the app, after registering everything from the service providers, Laravel registers the rest of the classes from that file.
That means that in your case, if you don't specifically register the Test class, it will still be resolvable. Basically, you only need to register classes that need some specific instructions to be resolved.
So how does Laravel resolve the dependencies?
When you run \App::make(Test::class) or inject dependency via type hinting in the constructor (the "better way" from my solution), Laravel looks for that dependency among the bindings.
When it finds the dependency, it resolves either the closure associated to it or the constructor of the class directly.
When it resolves the constructor directly, it looks for type hints among the constructor parameters and recursively resolves all of them until there's nothing else to resolve.
After that it returns the resolved class.
Of course, bear in mind that for Laravel to analyze the contructor of a class, it needs to be resolved via the service container in the first place. You can't just call $test = new Test(); and expect Laravel to do all the magic :)
Conclusion
This is a rather quick overview of Laravel's service container. The best way for you to learn it is, of course, studying the sources for yourself. It's truly elegant and it uses PHP's functionality to the fullest.
I really hope this shed some light on the service container for you and can help you in the future :)
The closure passed to the bind() method is not executed until you actually attempt to resolve the alias you are binding.
So, if you dd('breaker') inside the closure, this won't actually get executed until Test is resolved (whatever your preferred resolution method is):
Service provider:
// bind the closure to the 'Test' alias
public function register()
{
$this->app->bind('Test', function ($app) {
dd("BREAKER");
return new Test();
});
}
Code that resolve Test alias:
// different ways of resolving the alias out of the container.
// any of these will execute the bound closure.
$test = resolve('Test');
$test = app('Test');
$test = app()->make('Test');
$test = \App::make('Test');
try:
$this->app->bind(Test::class, function ($app) {
return new Test();
});

Laravel 4 Aliases in custom classes

i want to use the alias classes on laravel 4 "facades" like App::method , Config::method.
Well the thing is that i create a custom class and i have to import the namespaces like
<?php
namespace Face\SocialHandlers;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Config;
class FacebookHandler implements SocialHandlerInterface {
public function registrar($perfil) {
Config::get('facebook');
}
}
is there any way to use those classes like in controllers or routes files of the framework ?
like
<?php
namespace Face\SocialHandlers;
//use Illuminate\Support\Facades\App;
//use Illuminate\Support\Facades\Config;
class FacebookHandler implements SocialHandlerInterface {
public function registrar($perfil) {
Config::get('facebook');
}
}
Cya
ps: sry for my english
You can use use Config; instead of the more verbose use Illuminate\Support\Facades\Config; and the autoloader should handle it correctly.
Just as a tip, you shouldn't hardcode dependencies in your code. Instead of using the facades, you could create an "ConfigInterface" to get the common dependencies you need. Then create a "LaravelConfig class" (Or Laravel4Config.php) and implement those methods.
For a Quick Fix Answer, "catch the underliying facade instance":
namespace Face\SocialHandlers;
//use Illuminate\Support\Facades\App;
//use Illuminate\Support\Facades\Config;
class FacebookHandler implements SocialHandlerInterface {
protected $config;
protected $app;
public function __construct()
{
$this->config = \Illuminate\Support\Facades\Config::getFacadeRoot();
$this->app = \Illuminate\Support\Facades\App::getFacadeRoot();
}
public function registrar($perfil) {
$this->config->get('facebook');
}
}
For a Real Answer, maybe tedious, but good in the long run, instead of using the facades use an interface.
interface SocialConfigInterface
{
public function getConfigurationByKey($key)
}
Then
class Laravel4Config implements SocialConfigInterface
{
protected $config;
public function __construct()
{
$this->config = \Illuminate\Support\Facades\Config::getFacadeRoot(); //<-- hard coded, but as expected since it's a class to be used with Laravel 4
}
public function getConfigurationByKey($key)
{
return $this->config->get($key);
}
}
And Your Code
namespace Face\SocialHandlers;
//use Illuminate\Support\Facades\App;
//use Illuminate\Support\Facades\Config;
class FacebookHandler implements SocialHandlerInterface {
protected $config;
public function __construct(SocialConfigInterface $config)
{
$this->config = $config;
}
public function registrar($perfil) {
$this->config->get('facebook');
}
}
This way, if you want to change between frameworks you just need to create a SocialConfigInterface Implementation, or imagine the scenario where Laravel 5 wont use Facades, you want your code to be independent of "outsider changes" this is inversion of control IoC
First run,
php artisan dump-autoload
This will add your class namespace to vendor/composer/autoload_classmap.php. Now locate the entry for your class in this classmap array and get the proper namespace from there.
For example, you will get something like,
'Illuminate\\Support\\Facades\\App' => $vendorDir . '/laravel/framework/src/Illuminate/Support/Facades/App.php',
For this particular entry you have an alias in app/config/app.php
'App' => 'Illuminate\Support\Facades\App',
At the same way, locate your entry and use an alias in app/config/app.php.
You just have to create a folder, or place a class wherever already is listed for autoload. Me, for exemple, have this class PDFMaker, that uses a DomPDF Laravel implementation. I created a folder named libraries and put the path to it (under the app folder) in the autoload:classmap key on composer.json
"autoload": {
"classmap": [
"app/commands",
"app/controllers",
"app/libraries",
"app/models",
"app/helpers",
"app/database/migrations"
]
I did the same with commands for artisan commands! When you do that, you only have to declare a new object for any class under that folder, or call it in a static way, if the class has defined static methods. Something like Class::method.
Hope it helps you, have a nice day! :D
EDIT: After that, don't forget the dump-autoload for placing the new class in autoload scope.
EDIT 2: Remember that once you've put the class on autoload, it will be in same scope the others, so you won't have to import it to other, neither others to it!
You can also prefix the class names with a backslash, to use the global namespace: \Config::get('facebook') and \App::someMethod() will work without the need to add a use statement, regardless of the file's namespace.

Resources