Build Yii 1.* with composer

Posted on Tue 08 April 2014 in Tech

While we're looking forward to Yii 2 coming alive, all of the posts on please do not use it for production**. So it might be a little while before we have in-built composer awesomeness with Yii. The guide online on integrating composer with yii does not seem particularly straight-forward and misses out on a lot of steps, hence this post. You basically need to have the right folder structure or things will start to break. This guide relies on some of phundaments packages to get things up and running, as the official yii composer project doesn't work anymore.

  • Create the root folder my_yii_install.
  • mkdir -p my_yii_install/protected/config
  • In my_yii_install/protected/ download the composer.phar and create a composer.json like so.
{
 "repositories": [
 {
 "type":"composer",
 "url": "http://packages.phundament.com"
 }
 ],
 "require": {
 "php": ">=5.3.2",
 "yiisoft/yii": "1.1.*",
 "yiiext/migrate-command": "0.7.*"
 },
 "config": {
 "bin-dir": "bin/"
 },
 "autoload": {
 "psr-0": {
 "config": "./"
 }
 },
 "scripts": {
 "pre-install-cmd": "config\\\\ComposerCallback::preInstall",
 "post-install-cmd": "config\\\\ComposerCallback::postInstall",
 "pre-update-cmd": "config\\\\ComposerCallback::preUpdate",
 "post-update-cmd": "config\\\\ComposerCallback::postUpdate",
 "post-package-install": ["config\\\\ComposerCallback::postPackageInstall"],
 "post-package-update": ["config\\\\ComposerCallback::postPackageUpdate"]
 }
 }
  • In my_yii_install/config add console.php
return array(
 'aliases' => array(
 'vendor' => 'application.vendor',
 ),
 'basePath' => dirname(__FILE__) . DIRECTORY_SEPARATOR .'..',
 'name' => 'My Awesome Yii Site',
 'components' => array(
 'db' => array(
 // MySQL
 'connectionString' => 'mysql:host=localhost;dbname=my_yii_database',
 'emulatePrepare' => true,
 'username' => 'root',
 'password' => 'root',
 'charset' => 'utf8',
 ),
 ),
 'params' => array(
 'composer.callbacks' => array(
 // args for Yii command runner
 'yiisoft/yii-install' => array('yiic', 'webapp', dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'),
 'post-update' => array('yiic', 'migrate'),
 'post-install' => array('yiic', 'migrate'),
 ),
 ),
 );
  • Now you'll need to add a file like the below to actually handle the new composer hooks you've created. This will basically run migrations once your dependencies have installed.

This is blantantly taken from phundaments ComposerCallback. I just thought I'd copy it here for completeness.

ComposerCallback.php

/**
 * Class file
 *“
 * @author Tobias Munk
 * @link [http://www.phundament.com/](http://www.phundament.com/)
 * @copyright Copyright © 2012 diemeisterei GmbH
 * @license [http://www.phundament.com/license](http://www.phundament.com/license)
 */

namespace config;
 use Composer\\Script\\Event;

/**
 * ComposerCallback provides composer hooks
 *
 * This setup class triggers \`./yiic migrate\` at post-install and post-update.
 * For a package the class triggers \`./yiic -\` at post-package-install and
 * post-package-update.
 * See composer manual (http://getcomposer.org/doc/articles/scripts.md)
 *
 * Usage example
 *
 * config.php
 * 'params' => array(
 * 'composer.callbacks' => array(
 * 'post-update' => array('yiic', 'migrate'),
 * 'post-install' => array('yiic', 'migrate'),
 * 'yiisoft/yii-install' => array('yiic', 'webapp', realpath(dirname(__FILE__))),
 * ),
 * )
 * );
  • composer.json
 "scripts": {
 "pre-install-cmd": "config\\\\ComposerCallback::preInstall",
 "post-install-cmd": "config\\\\ComposerCallback::postInstall",
 "pre-update-cmd": "config\\\\ComposerCallback::preUpdate",
 "post-update-cmd": "config\\\\ComposerCallback::postUpdate",
 "post-package-install": [
 "config\\\\ComposerCallback::postPackageInstall"
 ],
 "post-package-update": [
 "config\\\\ComposerCallback::postPackageUpdate"
 ]
 }
*
 *
 * @author Tobias Munk
 * @package phundament.app
 * @since 0.7.1
 */

defined('YII_PATH') or define('YII_PATH', dirname(__FILE__).'/../vendor/yiisoft/yii/framework');
 defined('CONSOLE_CONFIG') or define('CONSOLE_CONFIG', dirname(__FILE__).'/console.php');

class ComposerCallback
 {
 /**
 * Displays welcome message
 * @static
 * @param ComposerScriptEvent $event
 */
 public static function preInstall(Event $event)
 {
 $composer = $event->getComposer();
 // do stuff
 echo "Phundament 3 Installernn";
 echo " * download packages specified in composer.json
 * trigger composer callbacksnn";
 if (self::confirm("Start Installation?")) {
 self::runHook('pre-install');
 } else {
 exit("Installation aborted.n");
 }
 }

/**
 * Executes ./yiic migrate
 * @static
 * @param ComposerScriptEvent $event
 */
 public static function postInstall(Event $event)
 {
 self::runHook('post-install');
 echo "nnInstallation completed.nnThank you for choosing Phundament 3!nn";
 }

/**
 * Displays welcome message
 *
 * @static
 * @param ComposerScriptEvent $event
 */
 public static function preUpdate(Event $event)
 {
 echo "Welcome to Phundament Installation 3 via composernnUpdating your application to the lastest available packages…n";
 self::runHook('pre-update');
 }

/**
 * Executes ./yiic migrate
 *
 * @static
 * @param ComposerScriptEvent $event
 */
 public static function postUpdate(Event $event)
 {
 self::runHook('post-update');
 echo "nnUpdate completed.nn";
 }

/**
 * Executes ./yiic -
 *
 * @static
 * @param ComposerScriptEvent $event
 */
 public static function postPackageInstall(Event $event)
 {
 $installedPackage = $event->getOperation()->getPackage();
 $hookName = $installedPackage->getPrettyName().'-install';
 self::runHook($hookName);
 }

/**
 * Executes ./yiic -
 *
 * @static
 * @param ComposerScriptEvent $event
 */
 public static function postPackageUpdate(Event $event)
 {
 $installedPackage = $event->getOperation()->getTargetPackage();
 $commandName = $installedPackage->getPrettyName().'-update';
 self::runHook($commandName);
 }

/**
 * Asks user to confirm by typing y or n.
 *
 * Credits to Yii CConsoleCommand
 *
 * @param string $message to echo out before waiting for user input
 * @return bool if user confirmed
 */
 public static function confirm($message)
 {
 echo $message . ' [yes|no] ';
 return !strncasecmp(trim(fgets(STDIN)), 'y', 1);
 }

/**
 * Runs Yii command, if available (defined in config/console.php)
 */
 private static function runHook($name){
 $app = self::getYiiApplication();
 if ($app === null) return;

if (isset($app->params['composer.callbacks'][$name])) {
 $args = $app->params['composer.callbacks'][$name];
 $app->commandRunner->addCommands(Yii::getPathOfAlias('system.cli.commands'));
 $app->commandRunner->run($args);
 }
 }

/**
 * Creates console application, if Yii is available
 */
 private static function getYiiApplication()
 {
 if (!is_file(YII_PATH.'/yii.php'))
 {
 return null;
 }

require_once(YII_PATH.'/yii.php');
 spl_autoload_register(array('YiiBase', 'autoload'));

if (Yii::app() === null) {
 if (is_file(CONSOLE_CONFIG)) {
 $app = Yii::createConsoleApplication(CONSOLE_CONFIG);
 } else {
 throw new Exception("File from CONSOLE_CONFIG not found");
 }
 } else {
 $app = Yii::app();
 }
 return $app;
 }

}
  • Now you should be able to run
php composer.phar install

and it should ask do you want to setup your yii project and all of the required folders, which is pretty awesome. The only other thing you might want to do is add more dependencies. Say you have a library you wish to add. Add the entry into composer.json run the update(which should call any migrations needed automatically), then you need to add this line to your index.php.

require_once(dirname(__FILE__).'/protected/vendor/autoload.php');

And should be be good to access composers namespaces libraries. Very cool!