How can I register a View::composer for use in a Laravel project from within a Laravel package?
In my package service provider I have a boot method with routes/views etc and this in the register function:
public function register()
{
$this->app->register(ComposerServiceProvider::class);
}
In the ComposerServiceProvider I have:
public function boot()
{
View::composer(
'admin.*', ProfileComposer::class
);
}
Which should load the ProfileComposer class into all admin.* views, but it's not working. It's definitely loading the class as a dd('Test'); in the boot method shows the 'Test' message in the browser, just not applying the view composer.
I can't see anything in the Laravel documentation regarding loading View Composers from packages
This code has been extracted from my working laravel project for use as a package going forward but the view composers are causing issues
A snippet from one of my own packages that works:
class ServiceProvider extends \Illuminate\Support\ServiceProvider
{
public function boot(Factory $view)
{
$view->composer('template::name', ProfileComposer::class);
}
}
The official docs don't mention packages, but it works exactly the same. The only difference is the place on the filesystem, but use the correct namespaces and you can just follow the docs: https://laravel.com/docs/5.8/views#view-composers
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 the following problem in my laravel 5 project. I have a service provider for form macros named MacroServiceProvider.php. Some macros should receive data from the database, I'm currently using the model and getting the results with eloquent but I want to use repositories instead, so I created my repository but I can't inject this directly to my service provider.
I want something like this:
...
public function register(MyRepoInterface $repo)
{
$registers = $repo->findAll();
Form::macro...
}
...
How can I do this?
Thanks.
I don't think you can do what are you asking, and I think you are misunderstanding the way providers work and what they are intended for.
In providers, you usually say what are the bindings among interfaces and implementations, so that when you do dependency injection in your application code, it works. I'm pretty sure they are not intended for doing real stuff.
For what you say about your code, I imagine something like this:
a repository interface (MyRepoInterface) with a real implementation using Eloquent (say EloquentMyRepo)
a facade, say Macro, so that you can do Macro::myMacro1(), Macro::myMacro2(), etc.
the methods myMacro1(), myMacro2(), etc, use the repository to get some data from the db and then call some methods from the Form facade
If I'm right, then I suggest something like this.
Repository
Define the interface in the file MyRepoInterface.php with
interface MyRepoInterface
{
public function findAll();
// ... your other repo methods
}
and an implementation EloquentMyRepo.php with
class EloquentMyRepo implements MyRepoInterface
{
public function findAll()
{
// ... do what you need
}
}
Facade
Define a facade file MacroFacade.php with this
use Illuminate\Support\Facades\Facade;
class MacroFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'macro';
}
}
Service class
Define your macro service class in a file MacroService.php, where you can use dependency injection and access your repository. In this class you define your myMacro1()... methods.
class MacroService
{
protected $myRepo;
public function __construct(MyRepoInterface $myRepo)
{
$this->myRepo = $myRepo;
}
public function myMacro1()
{
// access the repo
$items = $this->myRepo->findAll();
// ... do something with $items and finally return a string
return Form::macro(...);
}
public function myMacro2($arg1, $arg2)
{
// ... use the parameters to do something else
}
}
Bindings
In your Providers/AppServiceProvider.php file, go to the register() method and add
public function register()
{
// ...
$this->app->bind('App\MyRepoInterface', 'App\EloquentMyRepo');
// ...
}
so that when you use MyRepoInterface in dependency injection, Laravel knows it has to use an instance of EloquentMyRepo.
Now, let's create a service provider for your macro service. Create a file Providers/MacroServiceProvider.php and put in it
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class MacroServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('macro', 'App\MacroService');
}
}
Now, when we need the facade that is registered as macro, an instance of MacroService is used.
Configuration
We finally need some changes to the configuration. Open the config/app.php file, add the new provider
...
'providers' => [
...
'App\Providers\AppServiceProvider',
...
'App\Providers\MacroServiceProvider',
],
(note that the MacroServiceProvider is declared after the AppServiceProvider.)
Add the alias for the facade:
'aliases' => [
...
'Macro' => 'App\MacroFacade',
],
Done!
What happens
Let's suppose you call
...
Macro::myMacro1();
...
in your code. How the right method is called?
Macro is an alias handled by the MacroFacade class
The facade is registered in the IoC with the macro name by the getFacadeAccessor() method of MacroFacade
The MacroServiceProvider registered the MacroService class as an implementation for macro
An instance of MacroService must be created, but it has MyRepoInterface as dependency
The AppServiceProvider said Laravel to use EloquentMyRepo when MyRepoInterfice is required
So an instance of EloquentMyRepo is created and it is used to create an instance of MacroService
Macro has been resolved to an instance of MacroService
Laravel calls the myMacro1() method of that instance
I hope this can clarify a bit what happens.
I'm migrating old code to 5.7, and I'm running into a weird issue when it comes to helpers. I'm working with two packages - let's call them A and B. Package A one implements a ServiceProvider. I would like to use it in a controller in package B, but it seems unable to find it properly. I'm configuring it in the install() function of package A, as follows
$providers = Config::get('providers');
if (!$providers) {
$providers = array('group_membership' => '\Concrete\Package\A\Src\GroupMembership\GroupMembershipProvider');
} else {
$providers['group_membership'] = '\Concrete\Package\A\Src\GroupMembership\GroupMembershipProvider';
}
Config::set('providers', $providers);
And in the ServiceProvider itself, I bind it accordingly;
<?php namespace Concrete\Package\A\Src\GroupMembership;
use \Concrete\Core\Foundation\Service\Provider as ServiceProvider;
class GroupMembershipProvider extends ServiceProvider
{
public function register() {
$this->app->bind('helper/group_membership', 'Concrete\Package\A\Src\GroupMembership\GroupMembershipProvider');
}
}
Then when I want to use in package A, this works just fine:
$helper = Core::make('helper/group_membership');
But when I do that in package B, it tells me:
Class helper/group_membership does not exist
What could I do, except for merging the two packages together? What may be causing this?
Providers cannot be registered like that since we consume that config entry way before we load packages.
The proper way to register a package is to instantiate a new \Concrete\Core\Foundation\Service\ProviderList and use the registerProvider method.
Here's a good example for how to do that
// Register our service provider
$list = new ProviderList(\Core::getFacadeRoot());
$list->registerProvider('Concrete\\Package\\LegacySample\\Libraries\\ServiceProvider');
I know that this question was asked so many times, but none of answers helped me.
I'm getting exception in Laravel 5
BindingResolutionException in Container.php line 785:
Target [App\Contracts\CustomModelInterface] is not instantiable.
What I've done without success:
Register App\Providers\AppRepositoryProvider in app.php providers
php artisan clear-compiled
Everything works if I replace interfaces on repositories in MyService, but I feel that it's wrong (should it be handled by IoC container?).
Structure:
app
- Contracts
- CustomModelInterface.php
- Models
- CustomModel.php
- Repositories
- CustomModelRepository.php
- Providers
- AppRepositoryProvider.php
- Services
- MyService.php
App\Contracts\CustomModelInterface.php
<?php namespace App\Contracts;
interface CustomModelInterface {
public function get();
}
App\Repositories\CustomModelRepository.php
<?php namespace App\Repositories;
use App\Contracts\CustomModelInterface;
use App\Models\CustomModel;
class CustomModelRepository implements CustomModelInterface {
private $Model;
public function __construct(CustomModel $model) {
$this->Model = $model;
}
public function get() {
return 'result';
}
}
App\Services\MyService.php (Keep business logic / layer between controller and repositories)
<?php namespace App\Services;
use App\Contracts\CustomModelInterface;
class MyService {
private $Model;
public function __construct(CustomModelInterface $customModel) {
$this->Model= $customModel;
}
public function getAll() {
return $this->Model->get();
}
}
App\Providers\AppRepositoryProvider.php
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppRepositoryProvider extends ServiceProvider {
public function boot() {}
public function register() {
$models = array(
'CustomModel'
);
foreach ($models as $idx => $model) {
$this->app->bind("App\Contracts\{$model}Interface", "App\Repositories\{$model}Repository");
}
}
}
My controller looks like:
<?php namespace App\Http\Controllers;
use App\Services\MyService;
class SuperController extends Controller {
private $My;
public function __construct(MyService $myService) {
$this->My = $myService;
}
public function getDetails() {
return $this->My->getAll();
}
}
composer.json
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/",
"App\\Models\\": "app/Models/",
"App\\Contracts\\": "app/Contracts/",
"App\\Repositories\\": "app/Repositories/"
}
},
Thank you everyone, but problem was in my AppRepositoryProvider. As it's binding exception, then obviously the problem was with binding :)
Correct file is:
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppRepositoryProvider extends ServiceProvider {
public function boot() {}
public function register() {
$models = array(
'CustomModel',
'CustomModel2',
'CustomModel3'
);
foreach ($models as $model) {
$this->app->bind("App\Contracts\\{$model}Interface", "App\Repositories\\{$model}Repository");
}
}
}
Note, that I'm using "App\Contracts\\{$model}Interface" (not escaping "{" symbol) and it generate correct string App\Contracts\CustomModelInterface instead of App\Contracts\{$model}Interface (with unexpected escaping).
Every time I create a new repository/contract pair I make sure I do the following:
check the classes used in the service provider (copy/paste the namespaces)
register a new binding in config/app.php
php artisan optimize
Many hours of useless debugging led me to this short checklist.
For me, I forgot to bind in app->providers->RepositoryServiceProvider
the repository like this in the register method
public function register()
{
$this->app->bind(
\App\Play\Contracts\PatientRepository::class,
\App\Play\Modules\PatientModule::class
);
}
Make sure your RepositoryServiceProvider is registered in AppServiceProvider.
public function register()
{
$this->app->register(RepositoryServiceProvider::class);
}
I got past this error running:
php artisan config:clear
php artisan clear-compiled
php artisan optimize
php artisan config:cache
Related to:
Target is not instantiable. Laravel 5 - App binding service provider
The problem is solved by adding your repository in app/providers/AppServiceProvider
like the example below.
public function register()
{
$this->app->singleton(UserRepository::class, EloquentUser::class);
}
Dont forget the name space
use Test\Repositories\EloquentUser;
use Test\Repositories\UserRepository;
It worked for me
On App\Services\MyService.php you are passing that interface with dependency injection which tries to instantiate that -
public function __construct(CustomModelInterface $customModel) {
$this->Model= $customModel;
}
which is wrong.
Try implement that in that class - class MyService implements CustomModelInterface { and use the function of that interface like -
$this->get();
Or you are using it - class CustomModelRepository implements CustomModelInterface {
So if you do -
public function __construct(CustomModelRepository $customModel) {
$this->Model= $customModel;
}
then also you can access the interface methods.
I've just experienced an issue similar to this and the cause of my error was that I had set $defer to true in the service provider class but I had not implemented the required provides() method.
If you have deferred the creation of your class until it is need rather than it being loaded eagerly, then you need to also implement the provides method which should simply return an array of the classes that the provider provides. In the case of an interface, I believe it should be the name of the interface rather than the concrete class.
E.g.
public method provides(): array
{
return [
MyInterface::class,
];
}
Current documentation: https://laravel.com/docs/5.5/providers#deferred-providers
I hope this helps somebody else.
Don't worry guys. I have a solution to your problem.
I have an example for you.
Step1: php artisan make:repository Repository/Post //By adding this command you can create a repository and eloquent files
Step2: After adding that file you have to add/use this repository in the controller in which you want to use.
for eg: use App\Repositories\Contracts\PostRepository;
Step3: After adding that repo in your controller if you will run the app you will get an error like " Interface is not instantiable". It comes because you have created a repo and used in a controller, but laravel don't know where this repository is register and bind with which eloquent. So that it throws an error.
Step4: To solve this error you have to bind your repo with your eloquent in AppServiceProvider.
E.g:
AppServiceProvider.php file
<?php
namespace App\Providers;
// **Make sure that your repo file path and eloquent path must be correct.**
use App\Repositories\Contracts\PostRepository; // **Use your repository here**
use App\Repositories\Eloquent\EloquentPostRepository; **// Use your eloquent here**
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider {
/**
* Register any application services.
*
* #return void
*/
public function register() {
**// And bind your repository and eloquent here. **
$this->app->bind(PostRepository::class, EloquentPostRepository::class);
}
}
Step5: After binding repo and eloquent you can use all method of repo in your controller. Enjoy.....
Please let me know if you have any query.
execute this command :
composer dump-autoload
this command will remap your laravel autoload classes together with all other vendor's i had same issue before and this did the trick you can use it together with "-o" param for optimization .
Note that this can also be caused by the _constructor on the class being declared private, or otherwise being blocked...
If it cant call the constructor, the binding will fail
I think the problem here is that you don't bind App\Contracts\CustomModelInterface to anything so Laravel tries to create instance of interface.
In App\Providers\AppRepositoryProvider.php you have only:
$models = array(
'Model'
);
but you should have in this array CustomModel also, so it should look like this:
$models = array(
'Model',
'CustomModel',
);
The last thing you do is to use the interface you bound to the repository.
Set it up and try running your laravel app to make sure you get no errors.
In my case I had a mismatch between my repository and interface.
interface UserRepositoryInterface{
public function get($userId);
}
class UserRepository implements UserRepositoryInterface{
public function get(int $userId);
}
As you can see the interface get method does not include a type hint but the UserRepository class' get method has a type hint.
You won't get this error if you immediately start to use your Interface Binding.
register a new binding in config/app.php
In my case I forgot use App\Repositories\UserRepository in App\Providers\AppRepositoryProvider.php
intelephense wasn't complaining and the error-message did not give me any clue, but somehow I found out that it's missing and adding this line did the trick
I had this error, and found out that I should restart the queue because it runs in the job:
php artisan queue:restart