I'm trying to develop a package in laravel 4 - my first attempt at a package.
I found a couple of tutorials which I've tried to follow:
http://jasonlewis.me/article/laravel-4-develop-packages-using-the-workbench
and
http://culttt.com/2013/06/24/creating-a-laravel-4-package/
and of course in the official documentation.
I've followed the basic structure to create the framework. However on loading the app I get a class not found error. This relates directly to the serviceprovider I have placed in the app.php file.
here's my entry in the providers array:
'Longestdrive\Calendar\CalendarServiceProvider'
My folder structure is:
laravel/workbench/longestdrive/calendar/src/Longestdrive/Calendar
My service provider has the following entries:
<?php namespace Longestdrive\Calendar;
use Illuminate\Support\ServiceProvider;
class CalendarServiceProvider extends ServiceProvider {
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = false;
/**
* Bootstrap the application events.
*
* #return void
*/
public function boot()
{
$this->package('longestdrive/calendar');
}
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
//
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides()
{
return array();
}
}
I've double checked to spelling and ran a composer dump-autoload both from the root of the project and the root of the package.
I've run out of ideas for solving the class not found any ideas where I've gone wrong?
The line producing the error is this one:
C:\wamp\www\googleapi\laravel\vendor\laravel\framework\src\Illuminate\Foundation\ProviderRepository.php
Any help appreciated
Thanks
Update:
I ran a composer update as suggested in the workbench/package folder with a response nothing to update. I then ran composer at the root of the project and an error was produced:
[RuntimeException]
Error Output: PHP Fatal error: Class 'Longestdrive\Calendar\CalendarServiceProvider' not found
in C:\wamp\www\googleapi\laravel\vendor\laravel\framework\src\Illuminate\Foundation\ProviderRe
pository.php on line 123
I probably posted the wrong error line earlier. The full exception response is:
Class 'Longestdrive\Calendar\CalendarServiceProvider' not found
THe error extract:
* #param \Illuminate\Foundation\Application $app
* #param string $provider
* #return \Illuminate\Support\ServiceProvider
*/
public function createProvider(Application $app, $provider)
{
return new $provider($app);
}
which I assume relates to the service provider loader not finding the CalendarServiceProvider?
I found that running composer install from within the workbench/[vendor]/[package] folder solved the problem.
I encountered the same error, so I went deeper on its flow to knew what happens.
So dissecting a little bit basically, in the bootstrap phase, when bootstrap/autoload.php is loaded it runs at the end:
if (is_dir($workbench = __DIR__.'/../workbench'))
{
Illuminate\Workbench\Starter::start($workbench);
}
This requires EVERY workbench/vendor/package/**/**/**/autoload.php he found (by using Symfony Finder Component)
$finder->in($path)->files()->name('autoload.php')->depth('<= 3');
That's important because it's expecting to find workbench/vendor/package/vendor/autoload.php.
Successively in bootstrap/start.php it gets the 'providers' defined in config/app.php and try to load each of them:
$providers = $config['providers'];
$app->getProviderRepository()->load($app, $providers);
and then in ProviderRepository.php
foreach ($providers as $provider)
{
$instance = $this->createProvider($app, $provider);
so we'll end up with:
public function createProvider(Application $app, $provider)
{
return new $provider($app);
where it tried to create an instance of a class isn't really autoloaded. And so that's why the exception thrown!
In conclusion...
As #Ray said, by removing his Service from 'providers' => array( no error is thrown cause return new $myServiceDeclaredInProviderArray($app); never fires for that service.
As #Andrew Holt said
I found that running composer install from within the workbench/[vendor]/[package] folder solved the problem.
He's absolutely right because this create the autoload vendor dir and files, and everything works as we expect it to because it finds the autoload files:
$finder->in($path)->files()->name('autoload.php')->depth('<= 3');
Me
php artisan dump-autoload works as well if you remove the service from the providers array
In addition to #ilpaijin's and #Andrew Holt's answer, there sometimes comes the need (when there's a new version of Laravel) to run composer update within the workbench/vendor/package folder.
Also, as noted here, the composer.json within the package must require the same version of illuminate/support as the one required of laravel/framework in the project root's composer.json.
Thanks to #biscii note that one should use:
"illuminate/support": "4.1.x"
instead of
"illuminate/support": "4.x"
Related
I've got a problem with symfony 3.4 I'm stuck with. I think i'm not getting symfony autowire right, but I can't find what's causing the error.
I have a fresh installation of symfony with only one addtional package installed: league/tactician-bundle
I try to inject it in constructor of DefaultController in the folowing way:
/**
* #var CommandBus
*/
private $bus;
public function __construct(CommandBus $bus)
{
$this->bus = $bus;
}
/**
* #Route("/", name="homepage")
*/
public function indexAction(Request $request)
{
dump($this->bus);die;
}
My services.yml is untouched. When I hit the controller I get following error: Cannot autowire service "AppBundle\Controller\DefaultController": argument "$bus" of method "__construct()" references class "League\Tactician\CommandBus" but no such service exists. You should maybe alias this class to the existing "tactician.commandbus.default" service.
When I define it in my services.yml like this League\Tactician\CommandBus: '#tactician.commandbus.default' everything seems to work, but this is very uncomfortable to define every service I need in this way. Is it the only way or am I missing somehting?
Thanks in advance!
I'm pretty sure you forgot to register the bundle, that's the only way i could repro the issue. Go to your AppKernel.php and add
new League\Tactician\Bundle\TacticianBundle()
in your $bundles array.
L.E. my bad, not the same error message. What version of the bundle are you using? I've got "^1.1" and worked flawless.
I started a project on Laravel 5.4 today and got a ServiceProvider problem.
Here my service provider :
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class WizamProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
//die('YESSS');
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
$this->app->bind('Wizam\Test', function()
{
if(class_exists("Domains\Domomat\Test"))
return new \Domains\Domomat\Test;
else
return new \Core\Classes\Test;
});
}
}
I added this provider into config/app.php (App\Providers\WizamProvider::class), dump my autoloader like twenty times, clear cache, clear config. Nothing happened.
Here my route :
Route::get('/sub', function()
{
$test = new \Wizam\Test();
echo $test->render();
});
When I go to '/sub', I got Class 'Wizam\Test' not found. I cannot see my error, can you help me ?
Thanks !
To use the container you cannot instantiate using the new command, you can either inject it through the constructor
__constructor(\Wizam\Test $test)
{
}
or using the app(\Wizam\Test::class) I believe is the other way to do it as mentioned in the comments.
If someone once will get error saying that your interface not found ('class not found'), during making service provider closure, make sure your interface and class located in app/ folder. It took a lot of time fixing this. Have a nice day.
P.S. answer is not related to topic, but this page is first I found when I started to google the solution, so I beg you leave this here.
So I'm building a Laravel package and there is a special features that requires updating composer by adding a psr-4 namespace which points to a directory in Laravel base path.
I have tried this so far but doesn't work.
$loader = include(base_path('vendor/autoload.php'));
$loader->add('Classes\Weather', base_path('modules'));
Later:
$weather = new Classes\Weather\WeatherSite();
You might check this thread, which gives some solutions:
https://github.com/composer/composer/issues/1906#issuecomment-51632453
After several trials and going through Composer documentation, I was able to come up with this which works:
NB: The reason why I needed this solution is to enable me add a Psr4 path automatically from a Laravel package without manually adding a specific path required in the package manually in composer.json
Add this in the boot method of your package service provider , mine is DigitlimitModuleServiceProvider
use Illuminate\Support\ServiceProvider;
class DigitlimitModuleServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
$autoload = require base_path('vendor/autoload.php');
$autoload->addPsr4('Digitlimit\\Module\\', base_path('modules'));
$autoload->register();
if(!file_exists(base_path('modules'))){
\File::makeDirectory(base_path('modules'));
//works as long as there is permission
}
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
}
}
so I have created my own blog package in a structure of Packages/Sitemanager/Blog I have a service provider that looks like the following:
namespace Sitemanager\Blog;
use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
class BlogServiceProvider extends LaravelServiceProvider {
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = false;
/**
* Bootstrap the application events.
*
* #return void
*/
public function boot() {
$this->handleConfigs();
$this->handleMigrations();
$this->handleViews();
$this->handleRoutes();
}
/**
* Register the service provider.
*
* #return void
*/
public function register() {
// Bind any implementations.
$this->app->make('Sitemanager\Blog\Controllers\BlogController');
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides() {
return [];
}
private function handleConfigs() {
$configPath = __DIR__ . '/config/blog.php';
$this->publishes([$configPath => config_path('blog.php')]);
$this->mergeConfigFrom($configPath, 'blog');
}
private function handleTranslations() {
$this->loadTranslationsFrom(__DIR__.'/lang', 'blog');
}
private function handleViews() {
$this->loadViewsFrom(__DIR__.'/views', 'blog');
$this->publishes([__DIR__.'/views' => base_path('resources/views/vendor/blog')]);
}
private function handleMigrations() {
$this->publishes([__DIR__ . '/migrations' => base_path('database/migrations')]);
}
private function handleRoutes() {
include __DIR__.'/routes.php';
}
}
Now, what i would like to do is run the migrations dynamically if they have never been run before or within an installation process i suppose. I've seen in older documentation you could so something like this:
Artisan::call('migrate', array('--path' => 'app/migrations'));
However, this is invalid in laravel 5, how can I approach this?
Artisan::call('migrate', array('--path' => 'app/migrations'));
will work in Laravel 5, but you'll likely need to make a couple tweaks.
First, you need a use Artisan; line at the top of your file (where use Illuminate\Support\ServiceProvider... is), because of Laravel 5's namespacing. (You can alternatively do \Artisan::call - the \ is important).
You likely also need to do this:
Artisan::call('migrate', array('--path' => 'app/migrations', '--force' => true));
The --force is necessary because Laravel will, by default, prompt you for a yes/no in production, as it's a potentially destructive command. Without --force, your code will just sit there spinning its wheels (Laravel's waiting for a response from the CLI, but you're not in the CLI).
I'd encourage you to do this stuff somewhere other than the boot method of a service provider. These can be heavy calls (relying on both filesystem and database calls you don't want to make on every pageview). Consider an explicit installation console command or route instead.
After publishing the package:
php artisan vendor:publish --provider="Packages\Namespace\ServiceProvider"
You can execute the migration using:
php artisan migrate
Laravel automatically keeps track of which migrations have been executed and runs new ones accordingly.
If you want to execute the migration from outside of the CLI, for example in a route, you can do so using the Artisan facade:
Artisan::call('migrate')
You can pass optional parameters such as force and path as an array to the second argument in Artisan::call.
Further reading:
https://laravel.com/docs/5.1/artisan
https://laravel.com/docs/5.2/migrations#running-migrations
For the Laravel 7(and probably 6):
use Illuminate\Support\Facades\Artisan;
Artisan::call('migrate');
will greatly work.
I've created provider class and put it into app/models/Providers directory:
<?php
//app/models/Providers/NiceUrlServiceProvider.php
namespace Providers;
use Illuminate\Support\ServiceProvider;
class NiceUrlServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('niceurl', function()
{
return new \Utils\NiceUrl();
});
}
}
and facade class in app/models/Facades directory:
<?php
// app/models/Facades/NiceUrl.php
namespace Facades;
use Illuminate\Support\Facades\Facade;
class NiceUrl extends Facade {
protected static function getFacadeAccessor() { return 'niceurl'; }
}
I have also edited app/config/app.php and added this as provider:
'providers' => array(
// default ones
'Providers\NiceUrlServiceProvider',
),
and added alias to Facade:
'aliases' => array(
// default ones
'NiceUrl' => 'Facades\NiceUrl',
),
When I try to run my app I get:
Class 'Providers\NiceUrlServiceProvider' not found
*
* #param \Illuminate\Foundation\Application $app
* #param string $provider
* #return \Illuminate\Support\ServiceProvider
*/
public function createProvider(Application $app, $provider)
{
return new $provider($app); // this line marked as causing problem
}
However if I comment the line where I add my provider and in public/index.php put this code at the end of file:
$x = new \Providers\NiceUrlServiceProvider($app);
$x->register();
echo NiceUrl::create('some thing');
it works without a problem, so it does not seem to be a problem with autoloading.
Also if I register provider manually using:
$app->register('Providers\NiceUrlServiceProvider');
echo NiceUrl::create('some thing');
at the end of public/index.php it is working without a problem.
Questions:
How to make it work?
Where should I hold providers/facades files? Here I put them into model directory into separate folders.
The solution was quite simple but it was not obvious at all. After adding your provider you need to run:
composer dump-autoload
in your main project directory. It will generate new autoload_classmap.php file that will include your service provider. composer-update in this case also will work but it's not neccessary and it will take much more time. It's quite strange that it's necessary when you put provider into app/config/app.php and it's not necessary when you manually register provider but this is how it works.
The solution was quite simple but it was not obvious at all. After adding your provider you need to run:
composer dump-autoload
when you add some folder or other classes which is not in your composer.json autoload you would add it then run the above command.