Making Websites With YiiBoilerplate
December 22, 2013
While making websites with Yii framework for medium-sized businesses over and over again, we adopted a standard way of structuring the project over time.
It was initially based on the following premises:
- Separate public (“frontend”) and administrator’s (“backend”) sides to different domains for security purposes.
- Have an application configuration modular, so we can have parts of it committed to VCS repository for everyone and parts of it being crafted for every specific developer.
This two premises have lead to significant changes from the traditional project structure, described in Yii tutorial. It even affected the terminology, creating two new terms “entry point” and “environment”.
For quite some time we were building Yii-based websites using this structure and it proved really effective. This article will describe to you the all-new YiiBoilerplate with all the new features added in that time.
We do really hope that you’ll benefit from this boilerplate project template.
Being a boilerplate
We must make a note first that YiiBoilerplate is neither a library nor an another framework. It’s a boilerplate, stuff you use as a starting point for your work.
As a consequence of this, do not bother thinking about “updates” or “future versions” of YiiBoilerplate after you started your project over it. Just adapt it to your needs as you wish.
What we’ll talk about
In this article we’ll explain how the YiiBoilerplate differs from the traditional way of structuring Yii applications (suggested to us by ./yiic webapp
and the default settings of core Yii framework classes).
But first of all, we’ll look at the expected workflow for making websites with YiiBoilerplate.
For whom this article is written
You are expected to have experience working with Yii already, as for brevity there will be no explanations about things like asset management or view files rendering. This article is for tech leaders who don’t want to reimagine initial project structure for another medium-sized Yii-based web application ever again.
Beware, that for really large complex applications traditional separation of classes by type, as “components”, “controllers”, “models” and such, will be unreasonable and most possibly you’ll need to switch to something like the screaming architecture to prevent getting lost in the amount of classes. YiiBoilerplate is not really meant for it, most possibly you’ll need to rip it apart completely to achieve that structure.
Working with YiiBoilerplate
Installation
-
Install Vagrant (not covered by this article).
-
Install Virtualbox (not covered by this article).
-
If you have PHP 5.4+ installed already (and you should have, because it’s awesome), you’ve just installed all prequisites for YiiBoilerplate.
-
Now just clone the YiiBoilerplate repo:
git clone git@github.com:clevertech/YiiBoilerplate.git <yourprojectname>
-
Inside cloned directory run and wait for complete:
vagrant up
-
You’re done. Open up the http://localhost:8080/. It’s your future frontend. Open up http://localhost:8081/. It’s your future backend. You can start working. Don’t forget to
vagrant halt
the virtual machine before turning off your workstation, virtualbox can fail to shut itself down in time beforekill -9
arrives.
Workflow
Let’s do the Acceptance Test-Driven Development with the boilerplate application we just have created. You can consult the GOOS book if you want to know more.
We included Behat and PHPUnit frameworks to support you in this workflow. We suggest Behat for you as Gherkin syntax for acceptance tests is the best both for your QA team and your client.
Suppose we’re implementing the user account management at a backend. We assume you have an experience working with Behat and PHPUnit or at least able to read their documentation.
-
At start of working session, launch Selenium at separate console (it’s a simplest method).
bin/selenium
This step can be tricky, though.
bin/selenium
is our own little launcher designed for systems having Java binary findable by/usr/bin/env java
invocation. You don’t have to use this launcher, just run Selenium server in some way. -
Write acceptance specification for your feature in the
tests/specs/features/<entry_point_name>/<FeatureName>.feature
file in Gherkin syntax native to Behat.vim tests/specs/features/backend/UserManagement.feature
-
Write all of the test code supporting your spec in
tests/specs/contexts/FeatureContext.php
. You can structure your context classes as you wish, of course.vim tests/specs/contexts/FeatureContext.php
-
Run tests and watch them fail.
bin/behat -p backend
This command will launch all of the tests for backend, so we’ll be able to catch regressions. As you can see in
behat.yml
we also havefrontend
profile, to test frontend-related features. -
Decide on the next unit of functionality you will need for user management.
Let’s pretend we’ll start right from the
UsersController
. -
Write the test for this unit in
tests/units/<TestName>Test.php
. It’s PHPUnit territory, so you just write normal PHPUnit unit tests.vim tests/units/UsersControllerTest.php
-
Watch newlyborn unit tests fail, too.
bin/phpunit
-
Happily enter red-green-refactor cycle until you’re satisfied with this component.
-
Repeat from 4 until your acceptance test will turn green.
It’s a bit messy because a really complex setup for acceptance tests with Behat and Selenium, but we made what we could to simplify your life here. All configuration files are already written and parts of the system already integrated. You do not need to install and maintain anything separate from what you have in your codebase.
Get a full health report for a project
We integrated almost everything from the PHP QA Tools project, over the Phing build system.
To get all possible static code analysis reports for your project and the API documentation, just run:
bin/phing
It will generate the following reports in the reports
folder:
- Report of the code style violations in Checkstyle format from PHP Code Sniffer
- Code coverage report in Clover format from PHPUnit
- Code duplications report in XML from PHP Copy-Paste Detector
- Codebase size report in XML from PHPLOC
- Various problems report in XML from PHP Mess Detector
- Report in XML from PDepend
- Two code metrics schematics in SVG from PDepend
- Code coverage report in HTML format from PHPUnit
- HTML pages tree with all of your codebase with all above problems highlighted from PHP Code Browser
- and, finally, the autogenerated API documentation in HTML from ApiGen.
We believe it’ll be sufficient for you to get an idea about the state of your codebase. Of course to generate code coverage reports this reporter has to run all unit tests, too, so you get regression testing as a side effect, assuming that your unit tests are really fast.
Overview
Now, let’s delve into internals.
YiiBoilerplate was designed for medium-sized Yii-based web applications of any kind. By “meduim-sized” we mean 10 to 100 unique routes. Again, it has a harness to support two-tier test-first development, with Behat for end-to-end acceptance tests and PHPUnit for both pure unit tests and integration tests.
Basically, YiiBoilerplate is a bunch of files and folders you commit to your VCS repo as your “initial commit”, then start working for real. It consists of a proof-of-concept website, having one-page blank frontend and an admin side with rudimentary UI and a password-based authentication already done.
You can read the whole “table of contents” for the various directories of the YiiBoilerplate in README.md
files inside that directories.
Folder structure
Here’s the toplevel directories, sorted to bring related directories together:
common - classes and settings which should be global
console - classes and settings for `yiic` console runner
backend - classes and settings specific for admin backend
frontend - classes and settings specific for public frontend
carcass - supplementary stuff for build system, also Vagrant harness
reports - will be populated by reports from static analysis
tests - end-to-end and unit tests will be here
bin - binaries for various supplementary libraries
vendor - supplementary libraries managed by Composer
You don’t get the reports
and vendor
folders initially, they’re autogenerated by other tools.
Also, you’ll get the whole bunch of different files in the root of codebase, sorry for that:
.gitignore - a lot of stuff in YiiBoilerplate is autogenerated, so let git know about it
README.md - it serves well as a project description page in GitHub repo
Vagrantfile - allows us to use amazing Vagrant harness
behat.yml - config file for Behat testing harness
build.xml - build script designed for Phing build facility
filesets.xml - lists of folders to pass to static code analysis tools
composer.json - description of our dependencies for Composer
composer.lock - this is what was installed by Composer on test machine
composer.phar - Composer itself
phpunit.xml - config file for PHPUnit testing harness
Let’s go over the different parts of this boilerplate structure one by one.
Entry points
We have three entry points to our application, backend
, frontend
and console
.
Backend and frontend are meant to be accessible by pointing a web browser to two different domain names, and console entry point is the yiic
tool.
Well, of course if all these parts would be totally separate, you’ll have three applications instead of one, so most possibly you’ll need a common place to store classes useful to all entry points. So, that’s what common
directory is for.
All of these four folders have similar set of subdirectories, with some differences:
components - for Yii app components and other globally scoped stuff
config - we'll look to config building later
extensions - for Yii extensions
models - for model classes
runtime - running Yii application requires this folder
It should be noted that “components” is initially meant for descendants of CApplicationComponent
, mentioned in the config. However, judging from experience, stuff like UserIdentity
and WebApplication
usually ends up here, too. Feel free to create a separate directory for them if you want.
backend
and frontend
, being web applications, have the following subdirectories also:
controllers - for controller classes and possibly for action classes, too
modules - for submodules
packages - source of assets to be copied to `assets` folders
views - for view files
widgets - for widget classes
www - publicly accessible directory facing the Web
Files you want directly accessible from the Web you put in the www
subdirectory of backend
and frontend
. assets
folder is in here, too.
The scripts which are true entry points to your application are backend/www/index.php
and frontend/www/index.php
. They are carefully crafted already so hopefully you’ll never need to touch them.
console
has some console-specific stuff:
commands - for command classes
migrations - migrations lie here, along for clean template for them.
Console entry point is already configured to use console/migrations
folder as a source of migrations. Also, we ended up with a lot more clean template for migration class, and configured console runner to generate migrations based on console/migrations/template/migration.php
file.
common
also has the following subdirectories, being common to everything:
actions - for controller action classes common to both frontend and backend
lib - for not composer-installable global libraries which are not "extensions"
messages - for i18n via `Yii::t()`
widgets - global `widgets` directory
packages - global `packages` directory
Configuration tree
Most complex part of the YiiBoilerplate application is the configuration, built from set of different parts.
Basically, configuration for backend, console and frontend entry points is being constructed from the following parts, later ones overriding previous ones:
- Base common config.
- Environment-specific common config.
- Local overrides for common config.
- Base entry point-specific config.
- Environment-specific entry point-specific config.
- Local overrides for entry-point-specific config.
For frontend entry point the corresponding files would be:
common/config/overrides/base.php
common/config/overrides/environment.php
common/config/overrides/local.php
frontend/config/overrides/base.php
frontend/config/overrides/environment.php
frontend/config/overrides/local.php
Local overrides and environment overrides can be absent.
You can trace the resulting tree of require
calls starting from frontend/config/main.php
file.
That’s the file you really use as the configuration file for application.
In reality it’s just a four-line builder constructing the resulting configuration tree from six different parts specified above.
Local overrides
Local overrides are simple. That’s the snippets of configuration containing the non-portable parts like database access credentials.
config/overrides
subdirectory in all of common
, frontend
, console
and backend
directories contains the local-example.php
file which you can copy as local.php
and immediately use.
These overrides are not to be committed to the repository as they contain the settings specific to each developer’s machine.
Environment overrides
Configuration snippets for different environments are placed inside config/environments
directories.
You can specify things there like the different database paths, caching mechanisms, some OS-specific parameters, or anything you want.
To activate the desired environment, you are expected to copy needed configuration snippet from inside config/environments
subdir and place it into config/overrides/
under the name environment.php
. As it’s an obviously mundane and boring to hell task it’s automated for you by invoking the following command:
bin/yiic environment set --id=<environmentname>
Of course, each config/environments
subdirectory in all of entry points should have a configuration snippet named <environmentname>
.
Environment overrides are to be committed to the repository as they contain the proven set of settings intended to adapt the application to different working conditions.
Nothing forces you to really use this system of environment-specific settings. Configuration builder will happily live without these files.
Vagrant
YiiBoilerplate includes Vagrant harness which you can use as you wish.
Vagrantfile
is set up to use the default precise64
box, which is Virtualbox image loaded with blank Ubuntu 12.04.
As YiiBoilerplate is a rudimentary web application, we prepared a set of scripts to deploy it to Vagrant virtual machine.
They are located at carcass/vagrant
subdirectory.
Two scripts, which are used as provisioning scripts for Vagrant, can be used as an examples of automatic deploy of the YiiBoilerplate application to any *nix-based system:
prepare-precise64.sh
is a script to install the required tech stack for common database-backed web application to Ubuntu 12.04: PHP 5.4, apache, mysql, git etc. and create the database.setup-app.sh
is a script to install the application to prepared system: generate configs, required runtime directories, install dependencies.
You are encouraged to read through them yourself, they’re not so hard to comprehend.
Composer
All 3rd-party components of YiiBoilerplate, including Yii itself, are managed by the Composer. You get Behat+Mink+MinkExtension, PHPUnit, full stack of PHP Quality Assurance toolchain, Phing, ApiGen, Yii and YiiBooster as your dependencies. Even Selenium was packaged into Composer so it’s being installed, too.
Using Composer greatly reduces the size of your application codebase checked into the repository.
To ensure that everyone in your team gets exactly the same versions of the 3rd-party software,
Composer generates a special file called composer.lock
, which you commit to the repository instead of the whole vendor
folder, and the presense of this file will indicate to Composer what exact versions of software to maintain in a given codebase.
YiiBoilerplate repo contains such a file so you can be reasonably sure that at least its developers managed to run boilerplate application using the set of dependencies specified in there.
composer.json
was tweaked so you will get all executables inside bin
subdirectory.
Phing
Most possibly you’ll need the build system for your application, so we included the PHP-based one, namely, Phing.
Build file included in YiiBoilerplate contains the targets allowing you to generate the comprehensive set of reports about the health of your application.
Results of running the default target by issuing bin/phing
from root of codebase was already described before.
Please note that the set of source directories for each different tool being run by Phing is specified in separate build file carcass/filesets.xml
.
We’re sorry, but various directories excluded from analysis you have to hack inside the main build file, in case you’ll change the structure of a project.
Yiic
Usual console runner from Yii was moved to bin
subdirectory. As Composer is configured to install executables into the same directory, it was done to prevent you from using the default console runner instead of the one built-in to YiiBoilerplate, which you have total control over.
Whole console
subdirectory is for this console entry point to the application.
So, to run any console command built-in to Yii or defined by you in console/commands
, you have to run bin/yiic <command>
from root of codebase (instead of more short ./yiic
).
We have found this an acceptable trade-off.
Behat
As an acceptance tests driver we included Behat+Mink+MinkExtension combo over the Selenium2 driver.
This gives you arguably the best PHP-based acceptance testing solution out there. Gherkin syntax allows you and your QA team and perhaps even your client to specify the desired behavior of the application in human language, which is the clear win. Selenium uses real browser to manipulate the web GUI of your application, and does this insanely fast, so you will not need to cope with any of shortcomings of the headless browsers like phantomJS or Zombie.
All required configuration was already done.
behat.yml
config file is placed into the root of codebase for your convenience, so you’ll be able to run Behat without the hassle of specifying the path to config file in command line arguments.
You need to do only one thing: place a config called behat-local.yml
into the root of codebase, in which you specify the only non-portable setting: base URL for Mink to be able to connect to your web application.
If you run Vagrant virtual machine, provisioning script will place the behat-local.yml
pointing to its URLs automatically. So you can look at carcass/vagrant/behat-local.yml
file to understand what is needed from you.
If you use the default setup based on Selenium, you have to run the bin/selenium
helper script
which just launches Selenium, taking up one console terminal.
All of your specs related to frontend are expected to be placed into tests/specs/frontend
.
You run them all using the simple invocation bin/behat -p frontend
.
All of your specs related to backend are expected to be placed into tests/specs/backend
.
You run them all using the simple invocation bin/behat -p backend
.
Both sets of the specs use the same context class located in the tests/specs/contexts/FeatureContext.php
. All of your test steps definitions should be placed there.
Please note that a single FeatureContext
class is just a starting point, nothing prevents you from structuring your acceptance test harness as you see fit.
PHPUnit
For unit testing we included the PHPUnit library as the Composer dependency.
Its executable is in bin
, along with all other executables,
and by default you run all unit tests at once, as they have to be crazy fast anyway.
Its phpunit.xml
config file is placed in the root of codebase for your convenience, so you’ll be able to run PHPUnit as bin/phpunit
and be freed from specifying the path to config file.
Config file we included in YiiBoilerplate does not have any code coverage setup definitions.
To get a code coverage you are expected to use Phing target named coverage
as follows: bin/phing coverage
, which specifies code coverage settings using command line switches.
Our intention was to make a harness to support only pure isolated unit tests, so you get totally clean environment inside test cases.
In case where you need the integration test, we prepared the bootstrap script for PHPUnit which does the common initialization of YiiBoilerplate application as defined in common/bootstrap.php
and does some tricks the same way yiit.php
script does. This bootstrap script is essentially the fourth entry point to your application.
So when you run:
bin/phpunit --bootstrap carcass/phpunit.bootstrap.php
You run your test cases in the environment where the Yii class is defined and all usual setup is done so you can freely instantiate WebApplication instances as you see fit and using any configuration you want in your tests.
YiiBooster
For backend side of the application, we included our other library, YiiBooster as a Composer dependency, and made the configuration required to attach it.
So, in effect, you’ll get the total power of YiiBooster to make the UI of your backend. You are expected to skim through the YiiBooster documentation to learn what widgets you get from this toolkit.
Frontend, in contrast, is completely blank HTML5Boilerplate, because judging from our own experience, public side of the application is unique for every project anyway, so default styles from Twitter Bootstrap will not find any place there.
License
All of the code by default is licensed by BSD license, as all opensource work from Clevertech.
However, as you most possibly will change everything inside the codebase over time, you can probably treat the code as being in public domain. Our terms and intention is that you can adapt anything inside YiiBoilerplate to your needs.
Conclusion
YiiBoilerplate was a lot of work. Most possibly, with relatively little effort it can even be detached from Yii completely, as the most part of the harness is framework-independent.
We are eager to see whether it’ll be of real help to you so clone the repo, build something over it and share your experience in some way! Issue reports on GitHub are welcome, too.