In the past few years, my work has been connected with the use of
CMS Drupal , but at leisure I studied and just started running projects on Python frameworks
Django ,
Flask and
Twisted . Now I decided to learn the basics of two or three popular PHP frameworks, and first I decided to study
Zend Framework 2 and
Yii .
In the process of familiarization with the Zend Framework 2, I studied the tutorial from the official site (
http://framework.zend.com/manual/2.2/en/user-guide/overview.html ), I looked through the framework documentation (
http: //framework.zend .com / manual / 2.2 / en / index.html ), read Michael Romer’s “Web development with Zend Framework 2” book and put together his own test application.
Having digested all this information, I came to the conclusion that the official tutorial on the framework is rather dry:
- it does not cover working with users, sessions and access rights,
- such a fundamental part of the framework as a ServiceManager is considered only in passing,
- as an interface with the database, the Table Gateway pattern (and its corresponding implementation in the framework) is unconditionally used,
- use the template engine built into the framework, which after Python's Jinja 2 seems completely uncomfortable and primitive,
- etc.
As a result, I was able to create a more or less satisfactory application in terms of functionality after reading the book.
')
In this article I want to give an example of developing a simple blog, it will have several differences from the official tutorial. First of all, I will try to focus on those issues that during the study seemed to me insufficiently disclosed in the official tutorial. In addition, I will use some technologies that are alternative to those used in the default Zend framework:
- Twig will be used as a template engine,
- for working with the database - Doctrine ORM ,
- I will use the existing ZfcUser and BjyAuthorize modules for authorization / authentication and distribution of access rights,
- I will also consider the development of our own form validators, View plug-ins and others.
The article was written by a newbie (in the Zend Framework) for newbies, so any criticism of the case and tips for improving the application are welcome.
You can find the project code on Github:
github.com/romka/zend-blog-example .
The article will be divided into 3 parts:
- in the first (current) part, I will look at the ZendSkeletonApplication structure,
- in the second part I will analyze the development of my own module (forms, work with the database using Doctrine ORM, development of the View plug-in),
- The third part will focus on working with users and the Twig template engine.
So let's get started.
Environment
I assume that you already have a configured web server (mine is available at
zblog.kece.ru ) and an empty database has been created in MySQL for this project. Due to the fact that I plan to use Doctrine for working with the database, the database can be any one supported by this ORM.
I assume that you have the source code for ZendSkeletonApllication or my tutorial on hand. You can take them here:
github.com/zendframework/ZendSkeletonApplication and here:
github.com/romka/zend-blog-example . In addition, I assume that you understand the MVC pattern, have experience with any template maker and form validator.
Zend Framework uses the awesome
Composer dependency manager, which also needs to be installed on your system. You can read more about Composer here in this article:
habrahabr.ru/post/145946 . In a nutshell, Composer is a command line utility that allows you to quickly and conveniently download and install external libraries on which your PHP project depends. At the entrance, the utility accepts a JSON file in an intuitive format containing a list of names and versions of dependencies, at the output it downloads and installs the necessary libraries and their dependencies freeing you from the routine work.
The text of the article will sometimes refer to the source code of external applications or files automatically generated by Composer. These files are not in the repository, so for a deeper study of the application developed in this tutorial, you will need to install the necessary software yourself.
ZendSkeletonApplication
Using the Zend Framework, you can design and create the structure of your application from scratch, but to study the operation of the system, it is better to use the
ZendSkeletonApplication preset . If you have configured Composer, just run the command:
php composer.phar create-project
(with proper configuration, the
php composer.phar command can be replaced simply with
composer , but later in the article I will cite the first option as more universal)
After its execution, you will receive an application with the following structure (I left only the most interesting directories and files):
config/ autoload/ global.php application.config.php module/ Application/ < Application > public/ css/ img/ js/ index.php vendor/ < , Zend Framework> composer.json init_autoloader.php
Now you can open the created project. My version is available at
zblog.kece.ru .
Let's take a closer look at the created directories and files.
Project structure
composer.json
Let's start with the
composer.json file in the project root. It has the following form:
{ "name": "zendframework/skeleton-application", "description": "Skeleton Application for ZF2", "license": "BSD-3-Clause", "keywords": [ "framework", "zf2" ], "homepage": "http:// framework.zend.com/", "require": { "php": ">=5.3.3", "zendframework/zendframework": ">2.2.0rc1" } }
This file sets the application parameters that will be used by Composer. The most interesting part of this config is the require section, which contains a list of external libraries on which the application depends. Now there is only Zend Framework 2 in the list of dependencies, but in the future, we will add some more dependencies here: Doctrine, Twig and others. After adding a new dependency, it will be enough to execute the command in the console:
php composer.phar update
and Composer will download the necessary libraries. All external dependencies are added to the
vendor directory, now we can see the zendframework and composer directories in it.
Document root
Document_root of our application is not in the root of the project, but in the
public directory, this is where the
index.php file is located - the entry point to our project and the web server must be configured to work with this directory.
Index.php performs several important tasks, the two most interesting of which are connecting external libraries, loading the application configuration and launching it.
To connect external libraries, the
init_autoloader.php file is
executed from the project root, which in turn calls
vendor / autoload.php and, after it, the file
vendor / composer / autoaload_real.php automatically generated by
Composer . This file defines autoload methods for external libraries loaded by the Composer. Thus, when we in the code of our modules will connect namespaces of the
Zend \ View \ Model \ ViewModel form, PHP will know in which files to search for the specified namespaces.
Line of code:
Zend\Mvc\Application::init(require 'config/application.config.php')->run();
loads application configuration files and launches it. During application initialization (calling the
Zend \ Mvc \ Application :: init () method), a
ServiceManager is created - a key object used in many parts of the application. By default, ServiceManager creates another key object - EventManager. In the future, when we will create our modules in their settings, we will inform the Service Manager what other objects need to be created for the operation of our module.
We will talk more about the Service Manager later, but now let's take a closer look at the
config directory. It contains the
application.config.php file, which returns an array with the application configuration that can tell a lot of interesting things.
The modules array contains a list of enabled modules. Now we have only one Application module enabled, but there will be more of them in the future.
The module_listener_options array contains two interesting elements - the module_paths and config_glob_paths arrays:
- module_paths tells you where to look for plug-ins, by default in the vendor directories - external dependencies, and module - here we will find modules developed by us for our project.
- Config_glob_paths contains path masks to module configuration files. The regular expression 'config / autoload / {, *.} {Global, local} .php' means that all files of the type module_name. {Global or local} .php, global.php, local.php from the config / autoload directory will be downloaded .
Thus, first of all, the settings are loaded from the file config / application.config.php, then the settings are loaded from the files config / autoload / {, *.} {Global, local} .php and, last of all, the settings set at the module level (about this we will talk later).
If there are two configuration files for the same module in the config / autoload: global and local (module_name.global.php and module_name.local.php), then the global file will be downloaded first, followed by the local, have priority over global ones.
The local.php and * .local.php files are included by default in the .gitignore file (if you use another version control system, you need to add them to exceptions manually) and they should be used only for settings that correspond only to the current environment. That is, if you have a project in one form or another running on several different platforms: production, test and developer sites, then in the global settings of the application you need to store data that is relevant for all the listed sites, and unique for each site, for example , data to access the database.
In addition to the configuration files global for the whole application, each module can have its own settings file. Such module configuration files are loaded in the order in which the list of active modules in the
application.config.php file is defined. Thus, if you want to override the settings of another module in your module, then your module should be lower in this list.
The last important and not yet considered directory is the modules directory. In this directory there will be modules developed by us for our application. ZendSkeletonApplication comes with one Application module, but before exploring its structure, let's understand what a ServiceManager is and why it is needed.
ServiceManager
The official documentation (
http://framework.zend.com/manual/2.2/en/modules/zend.service-manager.intro.html ) states that the ServiceManager is a component that implements the Service Locator pattern for extracting other objects. . In other words, this is some entry point that allows you to access any objects registered in Service Manager from any location of the application.
Objects are registered in Service Manager either in the configuration file
module.config.php in the service_manager section, or in
Module.php in the getServiceConfig () method, it looks like this (example is copied from the documentation):
<?php // a module configuration, "module/SomeModule/config/module.config.php" return array( 'service_manager' => array( 'aliases' => array( // 'SomeModule\Model\User' => 'User', ), 'factories' => array( // — , // — , FactoryInterface, // , FactoryInterface, // PHP 'User' => 'SomeModule\Service\UserFactory', 'UserForm' => function ($serviceManager) { $form = new SomeModule\Form\User(); // Retrieve a dependency from the service manager and inject it! $form->setInputFilter($serviceManager->get('UserInputFilter')); return $form; }, ), 'invokables' => array( // — , // — , . 'UserInputFiler' => 'SomeModule\InputFilter\User', ), 'services' => array( // — , // — . 'Auth' => new SomeModule\Authentication\AuthenticationService(), ), ), );
Having the above configuration Service Manager in any of our controller, we can now call the code of the form:
$user_form = $this->getServiceLocator()->get('UserForm');
and the $ user_form object will contain the corresponding form.
Now it is time to return to the Application module included in ZendSkeletonApplication.
Application Module
The directory tree of this module looks like this:
config/ module.config.php language/ < *.po > src/ Application/ Controller/ IndexController.php view/ application/ index/ index.phtml error/ 404.phtml index.phtml layout/ layout.phtml Module.php
Let's start with Module.php. This file declares the Module class with 3 methods:
- onBootstrap ();
- getConfig ();
- getAutoloaderConfig ().
The code of the onBootstrap () method in this module is responsible for maintaining the routes of the Segment type (on the types of routes just below).
The getAutoloaderConfig () method tells the framework framework where to look for the source code for the module:
public function getAutoloaderConfig() { return array( 'Zend\Loader\StandardAutoloader' => array( 'namespaces' => array( __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__, ), ), ); }
In the current case, this is the
modules / Application / src / Application directory.
In the getConfig () method, the path to the module settings file is returned:
return include __DIR__ . '/config/module.config.php';
This file returns an array with settings containing the following data:
- The array element of the router is an array containing the list of routes served by the module and a list of controllers for each of the routes,
- view_manager contains paths to the templates that will be used by the application and a number of additional template settings,
- service_manager - a list of objects that must be initialized by the Service Manager,
- a number of other settings.
As in other MVC frameworks, the Zend Framework uses the concepts of: route (route), controller, and action (action). The route determines the address of the page, the controller and the action - the actions that will be performed to display the page. Each controller is a class, and actions are methods with the name of the form nameAction () in it. As a result, each controller can contain several actions, for example, the BlogPostController () controller that we will create next will contain the actions addAction (), editAction (), deleteAction (), viewAction () and indexAction ().
In the settings of the Application module, two routes are defined:
return array( 'router' => array( 'routes' => array( 'home' => array( 'type' => 'Zend\Mvc\Router\Http\Literal', 'options' => array( 'route' => '/', 'defaults' => array( 'controller' => 'Application\Controller\Index', 'action' => 'index', ), ), ), 'application' => array( 'type' => 'Literal', 'options' => array( 'route' => '/application', 'defaults' => array( '__NAMESPACE__' => 'Application\Controller', 'controller' => 'Index', 'action' => 'index', ), ), 'may_terminate' => true, 'child_routes' => array( 'default' => array( 'type' => 'Segment', 'options' => array( 'route' => '/[:controller[/:action]]', 'constraints' => array( 'controller' => '[a-zA-Z][a-zA-Z0-9_-]*', 'action' => '[a-zA-Z][a-zA-Z0-9_-]*', ), 'defaults' => array( ), ), ), ), ), ), ), );
- For the route / (site root, type Literal) is the Application \ Controller \ Index controller.
- for routes of the form / application / [: controller [/: action]] (Segment type) - the controller and action passed as arguments, that is, inside the module it is enough to define new controllers and actions and they will immediately be available at the described address. Due to this, the route / here is identical to the route / application / index / index.
There are several types of routes, their description can be found in the documentation:
framework.zend.com/manual/2.2/en/modules/zend.mvc.routing.html .
The Application module contains a single IndexController () controller with a single indexAction () action, which returns a page with a welcome message. To display this page, templates located in the view directory of the module are used.
At this point, I would like to complete the first part of the article, if it turns out to be in demand, I will complete and publish the remaining two parts, containing an example of developing a custom module and describing the mechanism for working with users.
upd:The second and
third parts of the article.