📜 ⬆️ ⬇️

Alternative organization of the project on Yii2


How is it proposed to create a project on Yii2 now? Choose a project template: basic or advanced, forate yourself, then write and commit there. Bam! Copy-paste happened, your project and template are now being developed separately. You will not receive the corrections made in the template, and in yii2-app-basic , of course, they will not take any modifications specific to your task. This is problem number one.


How is the project expanding on Yii2? Choose the appropriate extensions and connect them with a composite. Find an example of the config of this extension in the README and copy-paste into the config of your application. Oops ... Again copy-paste. Vylayaschaya different sides, including this: in a large project uses many extensions - the application configuration becomes huge and just unreadable. This is problem number two.


How are these problems related? The first is solved as follows: select the reused code and turn it into an extension. Hello again: the extension has its own config - got a second problem.


Most acutely these problems are for reusable solutions, when you need to raise a lot / a few, in principle, the same projects, but with large / small changes. Plus, getting rid of copy-paste and re-using the code has never stopped anyone.


I want to share my solution to these problems.



Plugin system


I will not torment the soul, I will state the essence right away, so that, if-what, it was convenient to spit it off right away, without reading it through.


So, the solution is to use the plugin system — from the very beginning, create your project as a plugin (extension along with the config), divide the project into plugins, and build the application config automatically from the config files of the plugins.


Here I have to stop and explain what I call the plugin. In yii2, extensions are provided ( yii2 extensions ) and they make it possible to organize reused code and connect it to the project with a composite. But more or less complex expansion needs to be configured. And here the framework does not help. The extension creator has two options:



I have already criticized the first option at the very beginning, I will take up the second:



In general, after going through several iterations, worn out with different options, a radical solution was born - to collect the config outside the application and even before its launch (hmm, it sounds simple and obvious, but, as you know, a good thought comes after). It turned out that it was most convenient to assemble the plugin to the compositor, from which there is convenient access to the entire hierarchy of project dependencies. So it turned out composer-config-plugin .


Composer Config Plugin


Composer-config-plugin works quite simply:



In composer.json extensions (which turns into a plugin), the following lines are added:


  "extra": { "config-plugin": { "web": "src/config/web.php" } } 

This means to put the contents of the src/config/web.php file into a config called web . And in this file it will be just what the plugin wants to add to the application config, for example, the internationalization config:


 <?php return [ 'components' => [ 'i18n' => [ 'translations' => [ 'my-category' => [ 'class' => \yii\i18n\PhpMessageSource::class, 'basePath' => '@myvendor/myplugin/messages', ], ], ], ], ]; 

Configs can be any number, including special ones: dotenv , defines and params . Configs are processed in this order:



So that the values ​​obtained in the previous steps could be used in all subsequent ones.


That is: environment variables can be used to assign constants. Constants and environment variables can be used to assign parameters. And the whole set: parameters, constants and environment variables can be used in configs.


In general, everything! composer-config-plugin simply merges all arrays of configs by analogy to the function yii\helpers\ArrayHelper::merge . Naturally, configs merzhatsya in the correct order - taking into account who is requesting someone - in such a way that the config of each package is merge after its dependencies and can overwrite the values ​​given by them. Those. the topmost package has full control over the config and controls all values, and the plugins only set default values. In general, the process repeats the assembly of configs in yii2-app-advanced , only on a larger scale.


Using trivially in an application is adding to web/index.php :


 $config = require hiqdev\composer\config\Builder::path('web'); (new yii\web\Application($config))->run(); 

Find more information and examples, as well as ask questions on the githaba: hiqdev / composer-config-plugin .


A very simple example of the hiqdev / yii2-yandex-metrika plugin . But he clearly demonstrates the possibilities of this approach. To get the Yandex.Metrica counter, it is enough to backup the plugin and set the yandexMetrika.id parameter. Everything! You do not need to copy-paste anything into your config, you do not need to add the widget to the layout - you do not need to touch the working code. A plugin is a solid piece of functionality that allows you to extend the system without making changes to the existing code.


- What? Can I write a new feature without breaking the old ones?
- Yes.
- Cool! Now you can not write tests?
- No ... It does not happen ...


Total, composer-config-plugin gives a system of plug-ins and solves the issue of reusing "small architectural forms" so to speak. It is time to return to the main thing - the organization of large reusable projects. I will repeat and clarify the proposed solution: create a project as a system of plug-ins, organized into the correct hierarchy.


Package hierarchy


The easiest way to organize a project is that our project restores the framework and third-party extensions with a composer (I call non-part of our project "third-party" , i.e. it turns out such a simple hierarchy of packages (repositories):



I skip all the intermediate options of the organization, tested and discarded according to the results of practical operation, and go straight to the optimal hierarchy, which we adhere to now:



The hierarchy displays who is requisitioning whom, i.e. The root project is the basic project, which in turn is the basic project, and the basic project is the framework.


- Whoa whoa! Easy! What is the "root" and "base project"?


I apologize, I invented everything myself, I did not find a suitable terminology, I had to cycle, I would be grateful for the best options.


“Root” I call the outermost package containing the code, config, and other files specific to this particular implementation of your project — how this version differs from the main project. Ideally, it contains literally several files, more on that below.


The “base project” is what yii2-app-basic turns yii2-app-basic in this diagram. Those. Reusable application framework that implements some basic functionality and is designed as a plug-in. This spare is optional, but very useful. You don’t have to do it yourself, it can be developed by the community as is being developed by yii2-app-basic . We are developing HiSite, more on that below.


Thus, packages form a hierarchy of composition — a more external package uses an internal one, mainly reusing its behavior, but redefining its specificity: the “root” uses and refines the main project, the main project - the basic, basic project - the framework.


It is necessary to clarify that we are talking only about the organization of the code, i.e. about the separation of code by packages / plugins. The architectural division of the project into layers, of course, regardless of the division into packages, but they can complement each other. For example, domain logic can be placed in a separate package for reuse between different projects.


- Ahhh! Need an example!


For example, you make business cards on a stream. The basic functionality is the same everywhere, but there are features for an additional fee, for example, a catalog and, of course, websites differ in appearance (theme) and a bunch of parameters. This can be organized into such a hierarchy of packages:



Hopefully, I didn’t discover America, and more and more less and so divide their projects into parts. Only, probably, without the "root" . I will try to convey its usefulness.


"Root"


In the "root" , just a couple of files are enough to be copied from the template and set up for this project installation. It is possible and desirable to do all three files:



Passwords can be put in .env and then used in params.php like this:


 return [ 'db.password' => $_ENV['DB_PASSWORD'], ]; 

Considering the " .env " of .env best candidates for taking away to .env are the parameters used by other (non-PHP) technologies.


Of course, it is possible and necessary to put in the "root" some config and even code specific to this installation, not subject to copy-pasting. As soon as I see copy-paste, I don’t like it terribly - I take it to some plugin.


The rest of the files and directories necessary for the functioning of the application ( web/assets/ , web/index.php ) are standard, you need to create and assign permissions as a "collector" (build tool, task runner), we are cycling our own, but this is a completely different story.


In essence, the “root” is params-local.php on steroids. It focuses on the difference between the specific installation of the project and the overall reused code. We create a repository under the root and store it on our private git-server, so we even commit secrets to it (but this is a holivar topic). All other packages are publicly available on GitHub. We commit composer.lock at the root, so transferring a project to another server is done simply by composer create-project (I know, the Docker will be better, but more on that next time).


- Can you be more specific? Show me the code finally!


HiSite and Asset Packagist


One of the "basic applications" that we are developing - HiSite hiqdev / hisite is the basis for a typical site, like yii2-app-basic, just made as a plugin, which gives all the advantages of reusing code over yii2-app-basic, :



The root template for a HiSite project here is hiqdev / hisite-template .


The dependency hierarchy looks like this:



The root README describes how to raise a project in your home — composer create-project plus configuration settings. Thanks to the implementation of the plugins and the hiqdev / yii2-thememanager theme library in root composer.json you can change yii2-theme-flat to yii2-theme-original to run composer update and the site will change into a new theme. Just like that.


Another real working project, suitable as an example, made using this approach and fully available on GitHub is the Asset Packagist , a packagist-compatible repository that allows you to install Bower and NPM packages as native composer packages.


The dependency hierarchy looks like this:



Details on how to raise the project are described in the root readme .


We will summarize


The topic is extensive, many details had to be omitted. I hope it turned out to bring a general idea. Once again, using the terminology entered:



We use the described approach for about a year, the impressions are very positive - the hair has become soft and silky: we divide and conquer, we rivet plugins easily and naturally, 100+ and are not going to stop, we need a new functionality - we make a new plug-in.


The approach, in one way or another, is applicable for other frameworks and even languages ​​... Oh, Ostap suffered ... That's enough for today! Thanks for attention. To be continued.


PS


Writing such volumes of text inspired a series of articles by Fabien Potencier (author of Symfony) about the upcoming Symfony 4. It's a shame to say, I didn’t fully understand how everything works, but caught ideas and goals: the bundle system will be refined towards their automatic configuration, which ultimately gives:


new way to create and evolve your applications with ease
a new way to create and develop your applications with ease

© Fabien Potencier


In general, I’m not the only one who considers the issues raised to be very important for the framework.


I love Yii. Let's do Yii better!


')

Source: https://habr.com/ru/post/329286/


All Articles