Official tutorial from which this is adapted:
Clone the project from Florent's repository (updated ZF3 quickstart):]
git clone [email protected]:Orkin/tsi-getting-start.git
composer update
Choose option 0
when asked Make your selection (default is 0):
(press enter as it is the default option).
When asked Remember this option for other packages of the same type? (y/N)
, type y
and press enter.
Create an album database and set your database access settings in the configuration file.
CREATE TABLE `album` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`artist` varchar(100) NOT NULL DEFAULT '',
`title` varchar(100) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Set your configuration credentials in the configuration.
The config/autoload/global.php
already contains the following code:
<?php
/**
* Global Configuration Override
*
* You can use this file for overriding configuration values from modules, etc.
* You would place values in here that are agnostic to the environment and not
* sensitive to security.
*
* @NOTE: In practice, this file will typically be INCLUDED in your source
* control, so do not include passwords or other sensitive information in this
* file.
*/
return [
'db' => [
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=album;host=localhost',
'username' => 'root',
'password' => '',
],
];
Add your own config in config/autoload/local.php
if needed:
<?php
return [
'db' => [
'username' => 'myuser',
'password' => 'mypassword',
],
];
Activate the development mode in ZF3:
composer development-enable
This command copy config/development.config.php.dist
to development.config.php
, hence allowing config file in autoload/{,*.}{global,local}-development.php
. It also disable the config cache.
modules.config.php
ZF2.5+ splitted in components that need to be activated individually in this file
Zend Developer Tools => gives details about the request, acl, db, etc. ZDT does not work for API development
Order of the files: global first, local next. Local files are not pushed to the repository (db connection and other secrets).
Read index.php:
- load an optional development config
- load the main config (application)
Both config contain instructions to retrieve config files from the autoload folder.
Zend Developer Tools quick start: copy configuration to the autoload folder.
Details the db configuration: how to set your local config
Composer scripts: development-enable, code style and so on.
Launch project using composer serve
.
Details module syntax:
- config
- src
- view
Best practice: separate config files by types to separate concerns
module.config.php
:
return [
'routes' => include 'routes.php',
'service_manager' => include 'service_manager.php',
];
routes.php
:
Explaination of the getting start routes.
Details the Application\config\module.config.php
.
Demonstration of a 404 by editing the routing. Turn on/off the display_not_found_reason and display exceptions. Explain the default config for 404 error pages and other error pages, and how you can override with a custom view.
template_path_stack easy configuration, good for rad but very bad perf (disk io).
template_map => precedence of the map on the stack, ocramius ocracachedviewresolver, allow easy override with map.
View helper: show paginator view, navbar helper. Only works in the template_map as naming is not specified, no way for the template_path_stack to resolve.
Module.php
: mandatory to make the module work. Does not do much necessarly. Read the module config files. Different interfaces exist, providing configuration for the module (ie. ADD FORM ELEMENT PROVIDER INTERFACE NAME HERE
).
Explain how config array cannot be cached when containing an anonymous function.
Back on composer for autoloading, with key PSR4. Show composer dump-autoload after removing a psr4 mapping, put it back, still not working, re-dump autoload and working.
The code provided does not contain a service layer, does not specifically follow the conventions.
AlbumController has a strong dependency on the AlbumTable => requires a factory. Demo: remove the table from the factory instanciation, refresh and crash.
The container know how to provide the object. Demonstration of how to chain the factories (Controller => Table => Gateway).
In 'service_manager', register new mapping in the container:
return [
AlbumTable::class => AlbumTableFactory::class,
AlbumGateway::class => AlbumGatewayFactory::class,
];
The container (by default), serve the object if already instanciated.
ZF2 had the service_manager invokables key for service with no dependencies. ZF3 introduces an InvokableFactory to push developers to use factory without creating the factory with no code inside.
Exercice: implement CGU in a specific module
Routes:
/cgu
/legal
Create the structure of your module:
- /module
- /Legal
- /config
- /src
- /view
- /Legal
Add the new module to the autoloader:
composer.json
(psr4 key):
{
"name": "zendframework/skeleton-application",
"description": "Skeleton Application for Zend Framework zend-mvc applications",
"type": "project",
"license": "BSD-3-Clause",
"keywords": [
"framework",
"mvc",
"zf"
],
"homepage": "http://framework.zend.com/",
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": "^5.6 || ^7.0",
"zendframework/zend-component-installer": "^1.0 || ^0.3 || ^1.0.0-dev@dev",
"zendframework/zend-mvc": "^3.0.1",
"zfcampus/zf-development-mode": "^3.0",
"zendframework/zend-db": "^2.8.1",
"zendframework/zend-mvc-form": "^1.0"
},
"autoload": {
"psr-4": {
"Application\\": "module/Application/src/",
"Album\\": "module/Album/src/",
"Blog\\": "module/Blog/src/",
"Legal\\": "module/Legal/src/"
}
},
"autoload-dev": {
"psr-4": {
"ApplicationTest\\": "module/Application/test/"
}
},
"extra": [],
"scripts": {
"cs-check": "phpcs",
"cs-fix": "phpcbf",
"development-disable": "zf-development-mode disable",
"development-enable": "zf-development-mode enable",
"development-status": "zf-development-mode status",
"post-create-project-cmd": [
"@development-enable"
],
"serve": "php -S 0.0.0.0:8080 -t public/ public/index.php",
"test": "phpunit"
},
"require-dev": {
"zendframework/zend-developer-tools": "^1.1.0"
}
}
And then composer dump-autoload
to activate the PSR4 autoloading change.
Create the controller (module/Legal/src/Controller/LegalController.php
):
<?php
declare(strict_types=1);
namespace Legal\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
final class LegalController extends AbstractActionController
{
public function cguAction()
{
return new ViewModel([]);
}
public function legalAction()
{
return new ViewModel([]);
}
}
module/Legal/config/module.config.php
:
<?php
declare(strict_types=1);
use Legal\Controller\LegalController;
use Zend\Router\Http\Literal;
use Zend\ServiceManager\Factory\InvokableFactory;
return [
'controllers' => [
'factories' => [
LegalController::class => InvokableFactory::class,
],
],
'router' => [
'routes' => include __DIR__.'/routes.config.php',
],
'view_manager' => [
'template_map' => [
'legal/legal/cgu' => __DIR__ . '/../view/legal/cgu.phtml',
'legal/legal/legal' => __DIR__ . '/../view/legal/legal.phtml',
],
],
];
module/Legal/config/routes.config.php
:
<?php
return [
'legal' => [
'type' => Literal::class,
'options' => [
'route' => '/legal',
'defaults' => [
'controller' => LegalController::class,
'action' => 'legal',
],
],
],
'cgu' => [
'type' => Literal::class,
'options' => [
'route' => '/cgu',
'defaults' => [
'controller' => LegalController::class,
'action' => 'cgu',
],
],
],
],
module/Legal/src/Module.php
:
<?php
declare(strict_types=1);
namespace Legal;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
final class Module implements ConfigProviderInterface
{
public function getConfig()
{
return include __DIR__.'/../config/module.config.php';
}
}
modules.config.php
(Legal at the end):
<?php
/**
* @link http://github.com/zendframework/ZendSkeletonApplication for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
/**
* List of enabled modules for this application.
*
* This should be an array of module namespaces used in the application.
*/
return [
'Zend\Form',
'Zend\Db',
'Zend\Router',
'Zend\Validator',
'ZendDeveloperTools',
'Application',
'Album',
'Blog',
'Legal',
];
module/Legal/view/legal/cgu.phtml
:
cgu
module/Legal/view/legal/legal.phtml
:
legal
Segment is used in other modules. In this specific case, the route is just a string so Literal.
REQUIRES EXAMPLE HERE.
For SEO reasons, using a semantic path in the URI is a best practice. Therefore, creating a Literal route for each endpoint.
Another solution to have semantic paths in URIs is to use child routes.
'legal' => [
'type' => Literal::class,
'options' => [
'route' => '/legal',
],
'may_terminate' => false,
'child_routes' => [
'legal' => [
'type' => Literal::class,
'options' => [
'route' => '/legal',
'defaults' => [
'controller' => LegalController::class,
'action' => 'legal',
],
],
],
'cgu' => [
'type' => Literal::class,
'options' => [
'route' => '/cgu',
'defaults' => [
'controller' => LegalController::class,
'action' => 'cgu',
],
],
],
],
],
More than just being an seo trick, having parent/child routes helps with prefixing.
Example with adding /legal/:lang in the child route, passing as a segment. Introducing the controller plugins $this->params()
, using the fromRoute
method and setting a default inline.
Add lang constraint ('lang' => '[a-zA-Z]*'
) and defaults
.
pls, share source code!!!