modules Best practices learned and gotchas on the way Squeezing the most out of Laravel Striking a balance between configurability and practicality Git, Composer, Packagist, Satis Assumes you know DI, facades. Some best practices have been avoided, implementations left out or very crude to save space. Every tutorial is wrong
for new projects, whether it's custom CMSes or something else You want to be able to do feature updates across multiple apps without copy-pasting code Why and when not? Each of your projects have too specific needs to split codebases into multiple repositories Makes more sense for a traditional HTML application than an API- centric application Maybe as API-centric server-side dev libraries catch up, more of this advice will be relevant You hate doing composer update
Stick to the default package directory structure Application has access to workbench classes, not vice-versa Don't rely on global namespace aliases If your package requires the entire laravel/framework package you will run into problems In the long run, aim to create packages without workbenches
system, more? Authentication and authorization (login and permissions) Do you really need controllers and views? Plan the public API methods/classes carefully - you'll hate yourself for them later on
return new PdfMaker(/* ... */); }); // allows $app['pdf'], App::make('pdf') $this->app->alias('Laracon\DemoPkg\PdfMaker', 'pdf'); } bind = factory -- bindShared = singleton Never resolve stuff from the IoC in register!
immediately $this->app['foo'] = new MyClass($this->app['bar']); // good - invoked when requested, deferred $this->app->bindShared('foo', function($app) { return new MyClass($app['bar']); }); }
other services via extend $this->app->extend('Laracon\DemoPkg\UserManager', function($userManager, $app) { $service = $app->make('Laracon\OtherPkg\SomeService'); $userManager->setOtherService($service); return $userManager; }); extend calls may sometimes have to be done in boot(), not register()
$this->app['router']->group(['prefix' => $prefix], function() { $this->registerRoutes(); }); } else { $this->registerRoutes(); } } protected function registerRoutes() { require __DIR__.'/routes.php'; } Anything you can put in app/routes.php or app/start/global.php you can put in boot Configurable route prefix - quick and dirty
{ $this->app->booted(function() { $this->app['url']->action('MyController@myMethod'); }); } register » boot » global.php, routes.php » booted booted callback means every other service provider ++ has been booted
in fine, but allow extension - use DI to make them swappable Extending and modifying concrete classes tends to break things. Write interfaces for models/repositories to be more flexible Include migrations, models/repositories, but be prepared for the package user modifying them
App::bind Route names as an abstraction Make URLs configurable to prevent conflicts Reading routes from config - github.com/anlutro/laravel-4-core, search for RouteProviderTrait
of CSS/JS frameworks/libraries - Difficult to have many CSS/JS dependencies without conflicts If you really need customizable classes/markup, consider writing dynamic widget/menu/form builder systems Strip the view of all logic, delegate to controllers and view composers Create your own shared utility classes in combination vith view composers
Collection; } } abstract class AbstractScriptComposer { final function compose($view) { $view->sidebar->add($this->getSidebarItems()); } protected abstract function getBodyScripts(); }
} public function createMenu($name) { return $this->menus[$name] = new MenuCollection; } } class MenuCollection { public function addItem($url, $title) { $this->items[] = new MenuItem($url, $title); } public function addSubmenu($title) { $this->items[] = new SubmenuItem($title, new MenuCollection); } } github.com/KnpLabs/KnpMenu
a must Extend regular PHPUnit_Framework_TestCase for unit tests To test controllers/other framework-dependant stuff: Everything in a service provider = your package is as easy to test as a regular app github.com/anlutro/laravel-testing
dependencies Controller tests with a seeded test database - thorough and easy Use phpunit's --coverage-html to make sure you're not leaving critical parts of your code untested
releases. Use semver Packagist runs cron jobs, can take up to 10 minutes for a new version to register Disadvantage: Packages have to be publicly accessible
goes down, host your own non- public packages Fastest setup: Set up web server with restrictions, put all git repos in webroot, run git update-server-info as a post-update hook Restrict per IP and/or use HTTP basic (composer can be configured to auto-auth using HTTP basic)
into Learn best practices, avoid some pitfalls/conveniences Plan public classes/methods, use semver, save yourself a headache Write widget/dashboard/sidebar/menu manager classes, add them to view object with creators, manipulate them in composers Unit/functional tests over browser testing