NOTE: This tutorial was written for Laravel version 4.0. I’ve added some notes for version 4.1 but they’re not tested yet. Do let me know if you see any problem.
When I first started using Laravel 4, I had been creating all my models, controllers and views in the root app folder. While things can get too comfortable and convenient here, it may not be a good idea to dump everything at the top. That’s when Package (or Bundle) comes in.
According to Laravel’s documentation:
“Packages are the primary way of adding functionality to Laravel.”
I found 2 great tutorials on creating your own Laravel 4 Package:
- http://culttt.com/2013/06/24/creating-a-laravel-4-package/
- http://jasonlewis.me/article/laravel-4-develop-packages-using-the-workbench
The following are some of the problems I’ve encountered and it took me quite some time to look for the solutions. Hope this will help someone else too.
1. Define your dependencies
A Package is like a sandbox module where it should contain its own dependencies (or vendors Packages). So you need to add your dependencies to your Package’s composer.json file.
As an example, I needed to include Cartalyst Sentry and Twitter Bootstrap to my Package, this is what I did under “require”:
"require": { "php": ">=5.3.0", "illuminate/support": "4.0.x","cartalyst/sentry": "2.0.*","twbs/bootstrap": "3.0.*" },
NOTE on Sentry:
After writing this blog, I’ve decided to move Sentry out of my package. Firstly, it will be cleaner and allow people using my package to choose their own authentication vendor. Secondly, I was getting so much trouble trying to get it to work properly. So it’s advisable to just put it on the Laravel app root instead.
Remember to run the following command line at your Package’s root folder:
php composer.phar update
php composer.phar dump-autoload
Note: On your first install, run “php composer.phar install”. Although using update will do the same thing too.
For dump-autoload, on the official documentation, they are using artisan instead of composer. I’m still not sure what’s the difference, but both seem to work.
php artisan dump-autoload
2. Extending Controller
When you download the Laravel 4 master, you will get a BaseController.php under the app\controllers folder. It looks something like this:
<?php class BaseController extends Controller { /** * Setup the layout used by the controller. * * @return void */ protected function setupLayout() { if ( ! is_null($this->layout)) { $this->layout = View::make($this->layout); } } }
And then all your custom controllers will extend this BaseController like this:
class CustomController extends BaseController {}
If you copy and paste this BaseController to your Package, it will not work. The Package will try to find “Controller” in your namespace Vendor\Package.
So to make it work, you must first define BaseController under your namespace, and then let Package knows that it needs to find the correct Controller.
In short, use this:
<?php namespace YourVendor\YourPackage; use \Illuminate\Routing\Controllers\Controller; class BaseController extends Controller { /** * Setup the layout used by the controller. * * @return void */ protected function setupLayout() { if ( ! is_null($this->layout)) { $this->layout = View::make($this->layout); } } }
Note for Laravel 4.1:
In Laravel 4.1, the Controller path has been moved. See http://laravel.com/docs/upgrade
Use this instead:
<?php namespace YourVendor\YourPackage; use \Illuminate\Routing\Controller; class BaseController extends Controller { // Your code }
3. Using Sentry in your Package
Just to be clear, as some readers mistaken “Sentry” as my Package’s name. It’s a third party Package created by Cartalyst: “Sentry is a simple, powerful and easy to use authorisation and authentication package.” I wanted to include this third party Package in my custom Package to add authorisation and authentication capability.
But once Sentry is downloaded and installed to my Package, I still couldn’t get it to recognise the alias “Sentry”. So the following code produces an error:
Sentry::logout();
The FatalErrorException message was:
Class ‘Vendor\Package\Sentry’ not found
That’s right, the Package has mistakenly treated Sentry under my Package’s namespace.
If you remember from Laravel’s basic documentation, while in the app’s root folder, we can define aliases such as “Sentry” in the \app\config\app.php file. But there doesn’t seem to have any way to register that in my Package.
So what I did, as a workaround, is to include the namespace at the top of my file. Like this:
<?php namespace YourVendor\YourPackage; use Cartalyst\Sentry\Facades\Laravel\Sentry; class CustomController extends BaseController { public function logOut() { Sentry::logout(); } }
Works like charm! But if you know of a shorter and better way, please leave me a message!
Better solution (edited)
Thanks to hardik dangar, there’s actually a shorter way to do this. Simply add a backslash before Sentry so that php doesn’t search Sentry within my namespace.
<?php namespace YourVendor\YourPackage; class CustomController extends BaseController { public function logOut() { \Sentry::logout(); } }
4. Using Illuminate Facades
Just as you thought everything is well and ready to code, you found yourself stuck at this line!
return View::make('vendorpackage::users/view');
And the FatalErrorException message is:
Class ‘Vendor\Package\View’ not found
What the?!
The same goes to Redirect, Input and Validator.
Base on my previous experience with Sentry, I got just the right idea.
Simply add the following to the top of your code:
<?php namespace YourVendor\YourPackage; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Redirect; use Illuminate\Support\Facades\Input; use Illuminate\Support\Facades\View; class CustomController extends BaseController {
Tada!!
Better solution (edited)
Thanks to hardik dangar, there’s actually a shorter way to do this. Simply add a backslash before View so that php doesn’t search View within my namespace.
return \View::make('vendorpackage::users/view');
5. Using Views in your Package
That right, you didn’t see it wrongly. This is how you call your Package’s view.
For example, we want view “users\view”, add your Package’s name to the front like this:
View::make('vendorpackage::users/view');
Then how about using master layout? Same logic, like this:
@extends('vendorpackage::layouts.master')
Where the directory is:
workbench\yourvendor\yourpackage\src\views\layouts\master.blade.php
6. Extending Eloquet in your own model
Laravel documentation taught us this:
class User extends Eloquent {}
This won’t work in a Package. Instead, you should do this:
<?php namespace YourVendor\YourPackage; use Illuminate\Database\Eloquent\Model; class User extends Model {}
7. Using Controller in your Package
This used to work:
{{ Form::open(array('action' => 'UserController@postStore')) }}
But to use your Package’s Controller, you need to add your Vendor\Package namespace to the Controller like this:
{{ Form::open(array('action' => 'YourVendor\YourPackage\UserController@postStore')) }}
8. Loading Package Config file
If you want to create a separate database for your package, you can define your database configuration within your package directory:
workspace\vendor\packagename\src\config\database.php
Then add this to the package service provider boot() method:
public function boot()
{
$this->package('vendor/package');
include __DIR__."/routes.php";
// Add my database configurations to the default set of configurations
$this->app['config']['database.connections'] = array_merge(
$this->app['config']['database.connections']
,Config::get('package::database.connections')
);
}
Source: http://stackoverflow.com/questions/15304722/eloquent-laravel-4-package-database-configuration-format
9. Loading external package within Providers and Aliases
In normal circumstances, when we add a new package to a Laravel installation, we would edit the config/app.php by adding the package namespace within the “Providers” and “Aliases” arrays.
However, when we need to add another external package that is a requirement within our own custom package, we wouldn’t want our users to edit a long lists of Providers and Aliases. Ideally, they should only include our custom package.
I’ve been searching for awhile and finally found a solution from this forum.
Solution:
In this example, we want to add an external package call “Markdown”.
In the file myLaravelProject/workbench/my-vendor/my-package/src/MyVendor/MyPackage/MyPackageServiceProvider,
put this in the boot() method:
$this->app->register('SomeExternalPackage\Markdown\MarkdownServiceProvider');
and this in the register() method:
$this->app->booting(function()
{
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
$loader->alias('MyPackage', 'MyVendor\MyPackage\Facades\MyPackage');
$loader->alias('Markdown', 'SomeExternalPackage\Markdown\Facades\Markdown');
});
10. Override config files of package
If you want to be able to allow the users who use your package to publish and modify the package’s config files, following this tutorial:
Fasil K K Cholakkara
September 27, 2013Each points are very useful. Expecting more tips and tricks from you.
fonky
January 4, 2014Hi! Thank you for this article. Do you know how I can migrate the Sentry tables in my package under vendor? On the command line “php artisan” is not working in my package directory.
Andrews Ang
January 9, 2014Hi there,
Did you specify the package name for Sentry?
“artisan” is at the root level, so to access it from your package, you need to point to the root of your application. E.g. (assuming your package is 3 levels down “workbench\vendorname\packagename”
from within your package directory, enter command:
php ../../../artisan migrate --package=cartalyst/sentry
Does this help?
Antony Try Wijaya
March 1, 2014that is not work, because the cartayst\sentry directory located within workbench\vendorname\packagename\vendor directory.
so when I run this at root folder laravel :
php ../../../artisan migrate -package=cartalyst/sentry
the result is : nothing to migrate.
can you tell me how you do that (migrate sentry table from package) ?
Andrews Ang
March 2, 2014Hi Antony,
Did you try to run that command within your package folder?
Alternatively, you can try putting Sentry on the Laravel app root instead of within the package. After writing this blog I’ve decided to take Sentry out of my package to make things simpler.
t2thec
February 5, 2014Hey, thanks for the post – Most enlightening!
I am having an issue extending BaseController from within the package. In your post here, I believe that the path isn’t quite right. Should it be the following?
\Illuminate\Routing\ControllerServiceProvider
In L4.1?
I am getting: Vendor/Package/Controller not found. You mentioned…
“then let Package knows that it needs to find the correct Controller.”
Could you elaborate on this for me? I’m a bit of n00b. Do I add something to the composer.json? Or my service provider?
Many thanks, keep up the good work!!
Andrews Ang
February 6, 2014H there,
This tutorial was written for Laravel 4.0.
In Laravel 4.1, the Controller path has been moved:
http://laravel.com/docs/upgrade
“If app/controllers/BaseController.php has a use statement at the top, change use Illuminate\Routing\Controllers\Controller; to use Illuminate\Routing\Controller;.”
There’re 2 ways to get the correct Controller class:
1. Define the path of Controller using “use” like this
use \Illuminate\Routing\Controllers\Controller; //<--- for version 4.0 user Illuminate\Routing\Controller; //<--- for version 4.1 class BaseController extends Controller { //Your code here } 2. Add a backslash to Controller class without using "use" like this class BaseController extends \Controller { //Your code here } I hope it helps. I've yet to upgrade my work to use version 4.1 yet. 🙂
ricardoibarrai
November 28, 2014Very useful man i was having trouble loading vendors inside my package Thanks for taking the time to post.