Plugins

CakePHP allows you to set up a combination of controllers, models,and views and release them as a pre-packaged application plugin thatothers can use in their CakePHP applications. If you’ve createdgreat user management, a simple blog, or web service adapters in one ofyour applications, why not package it as a CakePHP plugin? This way youcan reuse it in your other applications, and share with the community!

A CakePHP plugin is separate from the host application itself and generallyprovides some well-defined functionality that can be packaged up neatly, andreused with little effort in other applications. The application and the pluginoperate in their own respective spaces, but share the application’sconfiguration data (e.g. database connections, email transports)

Plugin should define their own top-level namespace. For example:DebugKit. By convention, plugins use their package name as their namespace.If you’d like to use a different namespace, you can configure the pluginnamespace, when plugins are loaded.

Installing a Plugin With Composer

Many plugins are available on Packagistand can be installed with Composer. To install DebugKit, youwould do the following:

  1. php composer.phar require cakephp/debug_kit

This would install the latest version of DebugKit and update yourcomposer.json, composer.lock file, updatevendor/cakephp-plugins.php, and update your autoloader.

Manually Installing a Plugin

If the plugin you want to install is not available onpackagist.org, you can clone or copy the plugin code into your pluginsdirectory. Assuming you want to install a plugin named ‘ContactManager’, youshould have a folder in plugins named ‘ContactManager’. In this directoryare the plugin’s src, tests and any other directories.

Manually Autoloading Plugin Classes

If you install your plugins via composer or bake you shouldn’t need toconfigure class autoloading for your plugins.

If we were installing a plugin named MyPlugin manually you would need tomodify your application’s composer.json file to contain the followinginformation:

  1. {
  2. "autoload": {
  3. "psr-4": {
  4. "MyPlugin\\": "plugins/MyPlugin/src/"
  5. }
  6. },
  7. "autoload-dev": {
  8. "psr-4": {
  9. "MyPlugin\\Test\\": "plugins/MyPlugin/tests/"
  10. }
  11. }
  12. }

If you are using vendor namespaces for your plugins, the namespace to path mappingshould resemble the following:

  1. {
  2. "autoload": {
  3. "psr-4": {
  4. "AcmeCorp\\Users\\": "plugins/AcmeCorp/Users/src/",
  5. "AcmeCorp\\Users\\Test\\": "plugins/AcmeCorp/Users/tests/"
  6. }
  7. }
  8. }

Additionally, you will need to tell Composer to refresh its autoloading cache:

  1. php composer.phar dumpautoload

Loading a Plugin

If you want to use a plugin’s routes, console commands, middleware, or eventlisteners you will need to load the plugin. Plugins are loaded in yourapplication’s bootstrap() function:

  1. // In src/Application.php
  2. use Cake\Http\BaseApplication;
  3. use ContactManager\Plugin as ContactManagerPlugin;
  4.  
  5. class Application extends BaseApplication {
  6. public function bootstrap()
  7. {
  8. parent::bootstrap();
  9. // Load the contact manager plugin by class name
  10. $this->addPlugin(ContactManagerPlugin::class);
  11.  
  12. // Load a plugin with a vendor namespace by 'short name'
  13. $this->addPlugin('AcmeCorp/ContactManager');
  14. }
  15. }

If you just want to use helpers, behaviors or components from a plugin you donot need to load a plugin.

There is also a handy shell command to enable the plugin. Execute the followingline:

  1. bin/cake plugin load ContactManager

This would update your application’s bootstrap method, or put the$this->addPlugin('ContactManager'); snippet in the bootstrap for you.

Plugin Hook Configuration

Plugins offer several hooks that allow a plugin to inject itself into theappropriate parts of your application. The hooks are:

  • bootstrap Used to load plugin default configuration files, defineconstants and other global functions.
  • routes Used to load routes for a plugin. Fired after application routesare loaded.
  • middleware Used to add plugin middleware to an application’s middlewarequeue.
  • console Used to add console commands to an application’s commandcollection.

When loading plugins you can configure which hooks are enabled. By defaultplugins without a Plugin Objects have all hooks disabled. New style pluginsallow plugin authors to set defaults, which can be configured by you in yourappliation:

  1. // In Application::bootstrap()
  2. use ContactManager\Plugin as ContactManagerPlugin;
  3.  
  4. // Disable routes for the ContactManager plugin
  5. $this->addPlugin(ContactManagerPlugin::class, ['routes' => false]);

You can configure hooks with array options, or the methods provided by pluginclasses:

  1. // In Application::bootstrap()
  2. use ContactManager\Plugin as ContactManagerPlugin;
  3.  
  4. // Use the disable/enable to configure hooks.
  5. $plugin = new ContactManagerPlugin();
  6.  
  7. $plugin->disable('bootstrap');
  8. $plugin->enable('routes');
  9. $this->addPlugin($plugin);

Plugin objects also know their names and path information:

  1. $plugin = new ContactManagerPlugin();
  2.  
  3. // Get the plugin name.
  4. $name = $plugin->getName();
  5.  
  6. // Path to the plugin root, and other paths.
  7. $path = $plugin->getPath();
  8. $path = $plugin->getConfigPath();
  9. $path = $plugin->getClassPath();

Using Plugin Classes

You can reference a plugin’s controllers, models, components, behaviors, andhelpers by prefixing the name of the plugin.

For example, say you wanted to use the ContactManager plugin’sContactInfoHelper to output formatted contact information inone of your views. In your controller, setHelpers()could look like this:

  1. $this->viewBuilder()->setHelpers(['ContactManager.ContactInfo']);

Note

This dot separated class name is referred to as plugin syntax.

You would then be able to access the ContactInfoHelper just likeany other helper in your view, such as:

  1. echo $this->ContactInfo->address($contact);

Plugins can use the models, components, behaviors and helpers provided by theapplication, or other plugins if necessary:

  1. // Use an application component
  2. $this->loadComponent('AppFlash');
  3.  
  4. // Use another plugin's behavior
  5. $this->addBehavior('OtherPlugin.AuditLog');

Creating Your Own Plugins

As a working example, let’s begin to create the ContactManagerplugin referenced above. To start out, we’ll set up our plugin’sbasic directory structure. It should look like this:

  1. /src
  2. /plugins
  3. /ContactManager
  4. /config
  5. /src
  6. /Plugin.php
  7. /Controller
  8. /Component
  9. /Model
  10. /Table
  11. /Entity
  12. /Behavior
  13. /View
  14. /Helper
  15. /templates
  16. /layout
  17. /tests
  18. /TestCase
  19. /Fixture
  20. /webroot

Note the name of the plugin folder, ‘ContactManager’. It is importantthat this folder has the same name as the plugin.

Inside the plugin folder, you’ll notice it looks a lot like a CakePHPapplication, and that’s basically what it is. You don’t have toinclude any of the folders you are not using. Some plugins mightonly define a Component and a Behavior, and in that case they can completelyomit the ‘templates’ directory.

A plugin can also have basically any of the other directories that yourapplication can, such as Config, Console, webroot, etc.

Creating a Plugin Using Bake

The process of creating plugins can be greatly simplified by using bake.

In order to bake a plugin, use the following command:

  1. bin/cake bake plugin ContactManager

Bake can be used to create classes in your plugin. For example to generatea plugin controller you could run:

  1. bin/cake bake controller --plugin ContactManager Contacts

Please refer to the chapterCode Generation with Bake if youhave any problems with using the command line. Be sure to re-generate yourautoloader once you’ve created your plugin:

  1. php composer.phar dumpautoload

Plugin Objects

Plugin Objects allow a plugin author to define set-up logic, define defaulthooks, load routes, middleware and console commands. Plugin objects live insrc/Plugin.php. For our ContactManager plugin, our plugin class could looklike:

  1. namespace ContactManager;
  2.  
  3. use Cake\Core\BasePlugin;
  4. use Cake\Core\PluginApplicationInterface;
  5.  
  6. class Plugin extends BasePlugin
  7. {
  8. public function middleware($middleware)
  9. {
  10. // Add middleware here.
  11. $middleware = parent::middleware($middleware);
  12.  
  13. return $middleware;
  14. }
  15.  
  16. public function console($commands)
  17. {
  18. // Add console commands here.
  19. $commands = parent::console($commands);
  20.  
  21. return $commands;
  22. }
  23.  
  24. public function bootstrap(PluginApplicationInterface $app)
  25. {
  26. // Add constants, load configuration defaults.
  27. // By default will load `config/bootstrap.php` in the plugin.
  28. parent::bootstrap($app);
  29. }
  30.  
  31. public function routes($routes)
  32. {
  33. // Add routes.
  34. // By default will load `config/routes.php` in the plugin.
  35. parent::routes($routes);
  36. }
  37. }

Plugin Routes

Plugins can provide routes files containing their routes. Each plugin cancontain a config/routes.php file. This routes file can be loaded when theplugin is added, or in the application’s routes file. To create theContactManager plugin routes, put the following intoplugins/ContactManager/config/routes.php:

  1. <?php
  2. use Cake\Routing\Route\DashedRoute;
  3. use Cake\Routing\Router;
  4.  
  5. Router:{plugin}(
  6. 'ContactManager',
  7. ['path' => '/contact-manager'],
  8. function ($routes) {
  9. $routes->get('/contacts', ['controller' => 'Contacts']);
  10. $routes->get('/contacts/{id}', ['controller' => 'Contacts', 'action' => 'view']);
  11. $routes->put('/contacts/{id}', ['controller' => 'Contacts', 'action' => 'update']);
  12. }
  13. );

The above will connect default routes for your plugin. You can customize thisfile with more specific routes later on.

Before you can access your controllers, you’ll need to ensure the plugin isloaded and the plugin routes are loaded. In your src/Application.php addthe following:

  1. $this->addPlugin('ContactManager', ['routes' => true]);

You can also load plugin routes in your application’s routes list. Doing thisprovides you more control on how plugin routes are loaded and allows you to wrapplugin routes in additional scopes or prefixes:

  1. Router::scope('/', function ($routes) {
  2. // Connect other routes.
  3. $routes->scope('/backend', function ($routes) {
  4. $routes->loadPlugin('ContactManager');
  5. });
  6. });

The above would result in URLs like /backend/contact-manager/contacts.

Plugin Controllers

Controllers for our ContactManager plugin will be stored inplugins/ContactManager/src/Controller/. Since the main thing we’llbe doing is managing contacts, we’ll need a ContactsController forthis plugin.

So, we place our new ContactsController inplugins/ContactManager/src/Controller and it looks like so:

  1. // plugins/ContactManager/src/Controller/ContactsController.php
  2. namespace ContactManager\Controller;
  3.  
  4. use ContactManager\Controller\AppController;
  5.  
  6. class ContactsController extends AppController
  7. {
  8. public function index()
  9. {
  10. //...
  11. }
  12. }

Also make the AppController if you don’t have one already:

  1. // plugins/ContactManager/src/Controller/AppController.php
  2. namespace ContactManager\Controller;
  3.  
  4. use App\Controller\AppController as BaseController;
  5.  
  6. class AppController extends BaseController
  7. {
  8. }

A plugin’s AppController can hold controller logic common to all controllersin a plugin but is not required if you don’t want to use one.

If you want to access what we’ve got going thus far, visit/contact-manager/contacts. You should get a “Missing Model” errorbecause we don’t have a Contact model defined yet.

If your application includes the default routing CakePHP provides you will beable to access your plugin controllers using URLs like:

  1. // Access the index route of a plugin controller.
  2. /contact-manager/contacts
  3.  
  4. // Any action on a plugin controller.
  5. /contact-manager/contacts/view/1

If your application defines routing prefixes, CakePHP’s default routing willalso connect routes that use the following pattern:

  1. /{prefix}/{plugin}/{controller}
  2. /{prefix}/{plugin}/{controller}/{action}

See the section on Plugin Hook Configuration for information on how to loadplugin specific route files.

For plugins you did not create with bake, you will also need to edit thecomposer.json file to add your plugin to the autoload classes, this can bedone as per the documentation Manually Autoloading Plugin Classes.

Plugin Models

Models for the plugin are stored in plugins/ContactManager/src/Model.We’ve already defined a ContactsController for this plugin, so let’screate the table and entity for that controller:

  1. // plugins/ContactManager/src/Model/Entity/Contact.php:
  2. namespace ContactManager\Model\Entity;
  3.  
  4. use Cake\ORM\Entity;
  5.  
  6. class Contact extends Entity
  7. {
  8. }
  9.  
  10. // plugins/ContactManager/src/Model/Table/ContactsTable.php:
  11. namespace ContactManager\Model\Table;
  12.  
  13. use Cake\ORM\Table;
  14.  
  15. class ContactsTable extends Table
  16. {
  17. }

If you need to reference a model within your plugin when building associationsor defining entity classes, you need to include the plugin name with the classname, separated with a dot. For example:

  1. // plugins/ContactManager/src/Model/Table/ContactsTable.php:
  2. namespace ContactManager\Model\Table;
  3.  
  4. use Cake\ORM\Table;
  5.  
  6. class ContactsTable extends Table
  7. {
  8. public function initialize(array $config): void
  9. {
  10. $this->hasMany('ContactManager.AltName');
  11. }
  12. }

If you would prefer that the array keys for the association not have the pluginprefix on them, use the alternative syntax:

  1. // plugins/ContactManager/src/Model/Table/ContactsTable.php:
  2. namespace ContactManager\Model\Table;
  3.  
  4. use Cake\ORM\Table;
  5.  
  6. class ContactsTable extends Table
  7. {
  8. public function initialize(array $config): void
  9. {
  10. $this->hasMany('AltName', [
  11. 'className' => 'ContactManager.AltName',
  12. ]);
  13. }
  14. }

You can use TableRegistry to load your plugin tables using the familiarplugin syntax:

  1. use Cake\ORM\TableRegistry;
  2.  
  3. $contacts = TableRegistry::getTableLocator()->get('ContactManager.Contacts');

Alternatively, from a controller context, you can use:

  1. $this->loadModel('ContactsMangager.Contacts');

Plugin Templates

Views behave exactly as they do in normal applications. Just place them in theright folder inside of the plugins/[PluginName]/templates/ folder. For ourContactManager plugin, we’ll need a view for our ContactsController::index()action, so let’s include that as well:

  1. // plugins/ContactManager/templates/Contacts/index.php:
  2. <h1>Contacts</h1>
  3. <p>Following is a sortable list of your contacts</p>
  4. <!-- A sortable list of contacts would go here....-->

Plugins can provide their own layouts. To add plugin layouts, place your template files insideplugins/[PluginName]/templates/layout. To use a plugin layout in your controlleryou can do the following:

  1. $this->viewBuilder()->setLayout('ContactManager.admin');

If the plugin prefix is omitted, the layout/view file will be located normally.

Note

For information on how to use elements from a plugin, look upElements

Overriding Plugin Templates from Inside Your Application

You can override any plugin views from inside your app using special paths. Ifyou have a plugin called ‘ContactManager’ you can override the template files of theplugin with application specific view logic by creating files using thefollowing template templates/plugin/[Plugin]/[Controller]/[view].php. For theContacts controller you could make the following file:

  1. templates/plugin/ContactManager/Contacts/index.php

Creating this file would allow you to overrideplugins/ContactManager/templates/Contacts/index.php.

If your plugin is in a composer dependency (i.e. ‘Company/ContactManager’), thepath to the ‘index’ view of the Contacts controller will be:

  1. templates/plugin/TheVendor/ThePlugin/Custom/index.php

Creating this file would allow you to overridevendor/thevendor/theplugin/templates/Custom/index.php.

If the plugin implements a routing prefix, you must include the routing prefixin your application template overrides. For example, if the ‘ContactManager’plugin implemented an ‘Admin’ prefix the overridng path would be:

  1. templates/plugin/ContactManager/Admin/ContactManager/index.php

Plugin Assets

A plugin’s web assets (but not PHP files) can be served through the plugin’swebroot directory, just like the main application’s assets:

  1. /plugins/ContactManager/webroot/
  2. css/
  3. js/
  4. img/
  5. flash/
  6. pdf/

You may put any type of file in any directory, just like a regular webroot.

Warning

Handling static assets (such as images, JavaScript and CSS files)through the Dispatcher is very inefficient. See Improve Your Application’s Performancefor more information.

Linking to Assets in Plugins

You can use the plugin syntax when linking to plugin assets using theView\Helper\HtmlHelper’s script, image, or css methods:

  1. // Generates a URL of /contact-manager/css/styles.css
  2. echo $this->Html->css('ContactManager.styles');
  3.  
  4. // Generates a URL of /contact-manager/js/widget.js
  5. echo $this->Html->script('ContactManager.widget');
  6.  
  7. // Generates a URL of /contact-manager/img/logo.jpg
  8. echo $this->Html->image('ContactManager.logo');

Plugin assets are served using the AssetMiddleware middleware by default.This is only recommended for development. In production you shouldsymlink plugin assets to improve performance.

If you are not using the helpers, you can prepend /plugin-name/ to the beginningof the URL for an asset within that plugin to serve it. Linking to‘/contact-manager/js/some_file.js’ would serve the assetplugins/ContactManager/webroot/js/some_file.js.

Components, Helpers and Behaviors

A plugin can have Components, Helpers and Behaviors just like a CakePHPapplication. You can even create plugins that consist only of Components,Helpers or Behaviors which can be a great way to build reusable components thatcan be dropped into any project.

Building these components is exactly the same as building it within a regularapplication, with no special naming convention.

Referring to your component from inside or outside of your plugin requires onlythat you prefix the plugin name before the name of the component. For example:

  1. // Component defined in 'ContactManager' plugin
  2. namespace ContactManager\Controller\Component;
  3.  
  4. use Cake\Controller\Component;
  5.  
  6. class ExampleComponent extends Component
  7. {
  8. }
  9.  
  10. // Within your controllers
  11. public function initialize(): void
  12. {
  13. parent::initialize();
  14. $this->loadComponent('ContactManager.Example');
  15. }

The same technique applies to Helpers and Behaviors.

Commands

Plugins can register their commands inside the console() hook. By defaultall shells and commands in the plugin are auto-discovered and added to theapplication’s command list. Plugin commands are prefixed with the plugin name.For example, the UserCommand provided by the ContactManager plugin wouldbe registered as both contact_manager.user and user. The un-prefixedname will only be taken by a plugin if it is not used by the application, oranother plugin.

You can customize the command names by defining each command in your plugin:

  1. public function console($commands)
  2. {
  3. // Create nested commands
  4. $commands->add('bake model', ModelCommand::class);
  5. $commands->add('bake controller', ControllerCommand::class);
  6.  
  7. return $commands;
  8. }

Testing your Plugin

If you are testing controllers or generating URLs, make sure yourplugin connects routes tests/bootstrap.php.

For more information see testing plugins page.

Publishing your Plugin

CakePHP plugins should be published to the packagist. This way other people can use it as composerdependency. You can also propose your plugin to the awesome-cakephp list.

Choose a semantically meaningful name for the package name. This should ideallybe prefixed with the dependency, in this case “cakephp” as the framework.The vendor name will usually be your GitHub username.Do not use the CakePHP namespace (cakephp) as this is reserved to CakePHPowned plugins. The convention is to use lowercase letters and dashes as separator.

So if you created a plugin “Logging” with your GitHub account “FooBar”, a goodname would be foo-bar/cakephp-logging.And the CakePHP owned “Localized” plugin can be found under _cakephp/localized_respectively.

Plugin Map File

When installing plugins via Composer, you may notice thatvendor/cakephp-plugins.php is created. This configuration file containsa map of plugin names and their paths on the filesystem. It makes it possiblefor plugins to be installed into the standard vendor directory which is outsideof the normal search paths. The Plugin class will use this file to locateplugins when they are loaded with addPlugin(). You generallywon’t need to edit this file by hand, as Composer and the plugin-installerpackage will manage it for you.

Manage Your Plugins using Mixer

Another way to discover and manage plugins into your CakePHP application isMixer. It is a CakePHP plugin which helpsyou to install plugins from Packagist. It also helps you to manage your existingplugins.

Note

IMPORTANT: Do not use this in production environment.