Chapter 13. Collaborative Work

This is the last chapter of the book, except the appendices (which have useful information too; don't skip them).

We will conclude the adventure we started 12 chapters ago with the features of Yii 2, which are not related to the business value of the application being constructed but are related to its infrastructure.

When working with the code base together with a team of other developers and routinely deploying code between test and production servers, you will inevitably face some problems you need to solve to continue effectively delivering new features.

First, we'll learn some nice tricks to manage the application configuration to accommodate different deploy targets.

Secondly, we'll talk about the database migrations we were using the entire time until now. Hopefully, we have enough practical knowledge about using migrations. Here, we'll talk about the reasons to use them and some nice tricks to maintain them more effectively.

Finally, we'll look at the Yii console applications, the side we implicitly used several times in this book but never explicitly talked about.

Configuration construction

We all know the following problem:

Imagine we are developing a web application, and we do it at our local workstation. The application uses the locally-installed MySQL instance with some particular database name, username, and a password. When we deploy this application, it'll use the MySQL instance installed at the deploy target, over which we can or cannot have control in choosing names and passwords. Even if we have total control over the deploy target and can use the same connection settings, it's highly impractical to enforce our own usernames and passwords on other people in the team, who are working on the same web application on their own workstations.

As the Yii application configuration is just a PHP script returning an associative array, we have a simple way of solving this problem: all pieces of configuration that should be specified for each deploy target individually can be moved to separate files. The main configuration will just include their contents using standard require() calls.

In fact, we already have such a setup in our example CRM application. The following are the lines of code that include other pieces of configuration into our main configuration file, which is ultimately passed to the constructor of yiiwebApplication:

return [
    // … global settings skipped here ...
    'components' => [
        'db' => require(__DIR__ . '/db.php'),
        // … a lot of other components skipped here …
        'assetManager' => [
            'bundles' => (require __DIR__ . '/assets_compressed.php')
        ],
    ],
    'extensions' => (require __DIR__ . '/../vendor/yiisoft/extensions.php')
];

We have already used separate configuration for database, compressed assets (from Chapter 8, Overall Behavior), and extensions.

Tip

It's probably the only place where an alternate syntax for the require() PHP built-in method looks useful. The (require PATH) notation explicitly expresses that we fetch something from PATH and put it right inside the brackets, which is arguably easier to read than the usual function-call notation.

Using the same technique, you can separate the declarations of the custom parameters in the params setting of the application. The Yii basic-application template uses this trick (see https://github.com/yiisoft/yii2/tree/master/apps/basic/config).

Configuration overriding can be raised to the next level if you take into account the array_merge_recursive() PHP built-in method and the yiihelpersArrayHelper::merge() method from the Yii framework. The ArrayHelper::merge() static function is especially useful. Instead of combining values with identical keys, array_merge_recursive() overrides the initial value with the new one.

How can it be used? Obviously, we can have the default application configuration in one file and deploy target-specific overrides in another file. Then the resulting configuration to be fed to the Yii web application instance will be constructed by ArrayHelper::merge(), as follows:

// config/web.php, as we always had
return yiihelpersArrayHelper::merge(
    (require "default.php"),
    (require "local.php")
);

Inside the local.php file, you maintain exactly the same structure as the default.php file, which is a lot easier to do than constantly remembering which part of the configuration lies in what configuration snippet. Of course, some files, such as the extensions.php file will be left as is. In case of the extensions.php file, we don't have control over its contents anyway.

Adding local overrides to the configuration

In Chapter 2, Making a Custom Application with Yii 2, we introduced a separate db.php configuration snippet to conform to the Yii basic application template, without really explaining exactly why we needed to separate this configuration at all. In fact, such a separation is a rudimentary step to making a code base more portable between developers and deploy targets. On each of them you can have different db.php scripts with credentials and the like. To achieve that, you cannot just commit this snippet to the version control system. However, such an approach has a serious drawback: each instance of the application should have a database configuration written manually from scratch. You need to communicate any common settings between developers in some other way than the code committed into repository, which means you cannot have any common settings committed to the repository, like maybe the custom database connection class or caching settings.

Let's apply the technique explained earlier to our code base. To do this, we need to make some really small changes:

  1. First, create a subdirectory named overrides inside the config directory. It's more correct to name it layers, though, but overrides implies the meaning that the snippets will override each other, while layers require additional understanding about the technique we are employing.
  2. Inside overrides create a file named base.php, that will hold the basic configuration for both console and web applications on any deploy target.
  3. We will move a really small number of configuration settings to config/overrides/base.php. They are as follows:
    return [
        'basePath' => realpath(__DIR__ . '/../../'),
        'components' => [
            'db' => [
                'class' => 'yiidbConnection'
            ],
        ]
    ];

In the Yii advanced application template, there are separate subdirectories for the console and two web applications, so basePath for them will be different. In our case, both applications share the basePath setting.

We have initialized the database connection component with just the name of the class. The credentials will be provided in later overrides. We need the database connection in the console application as well as in the web application because of migrations.

Here are the steps to perform it:

  1. Move the web.php configuration file to the config/overrides/web_base.php file. This name is chosen because we will recreate the config/web.php file later, and it's generally not so good to have two files in the code base sharing a name, even if they are in different folders.
  2. Inside web_base.php, we need to remove parts of the configuration already defined in the base configuration, which are just the basePath and components.db settings.
  3. Apart from that, it's important to change the paths to the extensions.php and assets_compressed.php configuration snippets inside web_base.php, as we are one folder deeper now. It's up to you whether to move assets_compressed.php somewhere, but it'll be a violation of the overrides concept to move this snippet to the overrides directory.
  4. Next, move the console.php configuration file to the config/overrides/console_base.php file. Similarly, remove the basePath and components.db settings from there.
  5. Now we arrive at the most interesting part. Create the config/overrides/local.php config file, with just the database connection settings in there as follows:
    return [
        'components' => [
            'db' => [
                'dsn' => 'mysql:host=localhost;dbname=crmapp',
                'username' => 'root',
                'password' => 'mysqlroot'
            ]
        ]
    ];


    Regardless of how specific the local overrides, you need to have exactly the same structure of the configuration file as in the main Yii configuration. The whole idea of overrides is based on this sharing of structure.

  6. With this local override in place, we can finally reconstruct config/web.php and config/console.php expected by our entry points. Here is how config/web.php will look:
    return yiihelpersArrayHelper::merge(
        (require __DIR__ . '/overrides/base.php'),
        (require __DIR__ . '/overrides/web_base.php'),
        (require __DIR__ . '/overrides/local.php')
    );

We just merge the three configurations, and the order is crucial. Now, if you open the website in a browser, nothing should change, which is exactly what we need. All tests should still pass as well.

In the same manner, construct config/console.php, except instead of the web_base.php, we'll use console_base.php.

These changes lead to important consequences. The main benefit is that we can now add to the version control system only the base and web base configuration overrides. The local override will be constructed on each deployment. To help developers and operation engineers, we can commit to the repository a specially prepared copy of local.php, with all exact credentials replaced by some meaningful placeholders. Such configuration templates are usually named after the file they represent, with the -example suffix appended, for example, local-example.php. Here is how we can format such an example in our CRM application:

<?php
/**
 * This is the example for local configuration overrides.
 * You must declare at least the database connection parameters.
 */

return [
    'components' => [
        'db' => [
            'dsn' => 'mysql:host=localhost;dbname=DB_NAME',
            'username' => 'DB_USERNAME',
            'password' => 'DB_PASSWORD'
        ]
    ]
];

Tip

So as not to commit the real local.php configuration snippet to the repository, in the Git version control system, you can add the .gitignore rule to it.

The advanced application template from the Yii bundle already uses a similar trick with overrides but only for custom application parameters (see, for example, https://github.com/yiisoft/yii2/blob/master/apps/advanced/frontend/config/main.php). To look at a real-world example of elaborated multilevel configuration building, you can turn to the YiiBoilerplate project from Clevertech at https://github.com/clevertech/YiiBoilerplate. It's for Yii 1.x, but the concept is the same, albeit a lot more extended.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.103.204