Posts tagged Symfony

symfony_logo

Symfony – Share Template Across Multiple Apps

0

I am currently working on a new project where I wanted multiple apps (frontend (fe) and backend (be)) to have the same exact appearance, and without having to duplicate the code in both template/layout.php files. So what I did is chose the fe layout.php file as the master template, then made the following changes:

// config/ProjectConfiguration.class.php
public function setup()
{
    ....

    sfConfig::set('masterTemplateUri', sprintf('%s/apps/fe/templates/layout.php', sfConfig::get('sf_root_dir'));

    ....
}

// apps/be/templates/layout.php
<?php include sfConfig::get('masterTemplateUri') ?>

Now I can share the same template across both fe and be apps and only have to make code changes in one location.

symfony_logo

PHP – Symfony – Speed up CLI Commands – Do Not Load Web Plugins

0

Below is a code snippet which I use to ensure that only necessary plugins are loaded when running symfony commands via Command Line Interface (CLI).

// config/ProjectConfiguration.class.php
class ProjectConfiguration extends sfProjectConfiguration
{
    public function setup()
    {
        ....

        //-----
        // Plugins
        //-----
        $basePlugins = array(
            'sfDoctrinePlugin',
            'userPlugin'
        );

        // Only add web plugins if we are not being accessed via cli
        if (php_sapi_name() === 'cli') {
            $webPlugins = array();
        } else {
            $webPlugins = array(
                'sfFormExtraPlugin',
                'sfJqueryReloadedPlugin',
                'icsicsBreadcrumbsPlugin'
            );
        }

        $this->enablePlugins(
            array_merge($basePlugins, $webPlugins)
        );
        //------

        ....
    }
}

Code is trivial, basically any plugins you need to add for CLI and web should be added to $basePlugins array, any web only plugins should be added to $webPlugins array as long as PHP is not running in CLI mode.

symfony_logo

PHP – Symfony – Build Model – No yml schema found

0

Working on a new project which requires more complex user account functionality than the current sfDoctrineGuard provides and stumbled upon this error message when attempting to build the model: “No yml schema found in /tmp/doctrine_schema_81014.yml”. There were two issues I had, which after resolving, allowed me to build my model as expected.

First, I added some custom code to my ProjectConfiguration.class.php which will only load certain plugins based upon whether I was running symfony from the Command Line Interface (CLI) or not. For example, when running commands from CLI, I don’t want to load sfFormExtraPlugin or sfJqueryReloadedPlugin, so I had to make sure that my custom plugin was added to the array. You can view the code snippet here (demonstrative only, has no impact on problem outlined in article).

Second, I had the schema.yml located @ plugins/userPlugin/config/doctrine/schema.yml, a uri which Symfony is unaware of. To make symfony aware of the custom location for my config files, I had to add the following code to plugins/userPlugin/config/app.yml:

# plugins/userPlugin/config/app.yml
all:
  userPlugin:
    config_dir: %SF_PLUGINS_DIR%/userPlugin/config
    recursive: true

The important settings is the `recursive: true` setting. After resolving both issues I cleared cache and was able to successfully build the model.

symfony_logo

Symfony – Propel – Determine Propel Version

0

I was recently working on a project with Symfony 1.2 and wanted to know the version of Propel that was bundled with it. After some toying around with the `php symfony help` command, I noticed that no commands regarding version fell under the propel header, I was hoping for something like `php symfony propel:version`. Because I still needed to know the bundled Propel version I ended up issuing the following command:


$ -> ack -i version /path/to/symfony/lib/plugins/sfPropelPlugin/lib/vendor/propel/Propel.php

* @version $Revision: 989 $
 * The Propel version.
 const VERSION = '1.3.0-dev';

According to the source code, the version of Propel bundled with Symfony 1.2 is 1.3.0-dev (yikes). So if you want to see the version of Propel bundled with the version of Syfmony you are using, just use the above `ack` (or fgrep) command.

On a side note, if you are using Symfony 1.x, you should look into the sfPropelORMPlugin, it will allow you to use the latest version of Propel (1.6) with Symfony 1.x, which is good because there are several new features in Propel 1.6 that make developing easier and improve performance. And they actually added the ability to view the version without having to ack the codebase.

symfony_logo

Symfony – Doctrine – Call to a member function evictAll on a non-object

1

If you are working with Symfony and Doctrine and encounter a “Call to a member function evictAll() on a non-object” error message, check to see if you are using DQL for any delete queries. I just came across this error and after some googling (mostly about bad schema files) I couldn’t fix the issue. I then checked a custom DQL statement which unlinks rows and saw that I had aliased the main table with ‘caa’, but the other references were ‘ca’ (only one a, instead of two). Once I fixed the alias issue, everything worked as expected.

Symfony – Standard API for Logging Using __callStatic

0

Last year I wrote an article on how to write a simple, standard logging class for Symfony (http://melikedev.com/2010/08/25/symfony-standard-api-for-logging/). This article will demonstrate how do to the same functionality in only a few lines of code using __callStatic(). For more info on __callStatic() check out http://melikedev.com/2011/10/31/php-__callstatic-method-overloading/

Before:

// located @ /lib/MyCustom/Api/Log.php
class MyCustom_Api_Log
{
    /**
     * Wrapper method for getting a symfony logger object
     *
     * @return object
     */
    public static function getLogger()
    {
        return sfContext::getInstance()->getLogger();
    }

    /**
     * Wrapper method for logging debug message
     *
     * @param string $message
     */
     public static function logDebug($message)
     {
         self::getLogger()->debug($message);
     }
    /**
     * Wrapper method for logging an error
     *
     * @param string $message
     */
     public static function logError($message)
     {
         self::getLogger()->err($message);
     }
}

// Example Usage
MyCustom_Api_Log::logError('Oh noes something went wrong');

After:

// located @ /lib/MyCustom/Api/Log.php
class MyCustom_Api_Log
{
    /**
     * Wrapper method for getting a symfony logger object
     *
     * @return object
     */
    public static function getLogger()
    {
        return sfContext::getInstance()->getLogger();
    }

    /**
     * Magic method for logging all supported log levels
     *
     * @param string
     * @param array
     */
     public static function __callStatic($level, $params)
     {
         // Only concerned with message at this time
         // However any number of params can be passed in
         $message = shift($params);

        self::getLogger()->$level($message);
     }
}

// All these calls are made to __callStatic, so no need for individual methods
MyCustom_Api_Log::err(array('Oh noes something went wrong'));
MyCustom_Api_Log::warn(array('Oh noes something went wrong'));
MyCustom_Api_Log::notice(array('Oh noes something went wrong'));

Symfony – Symfony 2 Security Audit Results

0

It appears Fabien Potencier (lead engineer for the Symfony Framework) enlisted the help of an outside firm to audit the security of the Symfony 2 code-base. He posted the results a few days ago @ http://symfony.com/blog/symfony2-security-audit. Overall, it looks like the Symfony 2 code-base made the grade with regards to security issues. I did pose the question if they conducted the same type of audit for the Symfony 1.4 codebase as it will still be officially supported until 2013. If/when I find out I will update this post.

I applaud his efforts to provide a robust and secure framework.

Symfony – Serve External Apis from External Hosts – jQuery – Yui

0

As a developer you have probably had to use a 3rd party API library to make your job easier. Take for example jQuery. jQuery abstracts many internal javascript constructs which make calling code much easier to work with, and jQuery works the same across most standard web browsers. Which brings me to the point of this post, what is the best method of bringing these 3rd party API libraries into your projects? Sure you could download the files and serve them from your own web-servers, but why? Why not let 3rd party API hosts such as Google or Yahoo, serve these API libraries for you?

Consider the following links:

Typical host construct: http://static.yourdomain.com/js/vendor/jquery/jquery.min.js

3rd party API host construct: http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js

What’s the difference between the two files? Nothing (given that you abstracted out the version number using a symlink in the first example). However, with the 3rd party API host you are using their resources vs using your own resources. So that means more http connections for serving actual content rather than serving API libraries. Also, serving API libraries from these external hosts also ensures faster delivery of the data because there is a high probability that the 3rd party host is using a CDN (or cloud) to serve the request.

Now we see why it’s better to use 3rd party API hosts to serve common API libraries, but how do we configure Symfony to serve these API libraries. We don’t want to litter our templates with url constructs, so we are going to add some code to our top level app.yml and view.yml files. If you are unaware, a flexible feature of using yml files vs xml or ini is that with yml files you can add php code, which will be executed when the yml files are parsed.

First, lets set our 3rd party API host, and jquery version via our app.yml file. Note: You can set these same values at the global config level (/config/app.yml vs /apps/<app_name>/config/app.yml) so all your apps will benefit.

#/apps/<app_name>/config/app.yml

all:
jquery_api_version: 1.6.2
google_ajax_libs_api_uri: http://ajax.googleapis.com/ajax/libs

Ok, now lets configure our view.yml file. Same note as above, this can be set at the global config level so all your apps will benefit.


#/apps/<app_name>/config/view.yml

default:

javascripts:

- <?php echo sfConfig::get('app_google_ajax_libs_api_uri') ?>/jquery/<?php echo sfConfig::get('app_jquery_api_version') ?>/jquery.min.js
- <?php echo sfConfig::get('app_google_ajax_libs_api_uri') ?>/jqueryui/<?php echo sfConfig::get('app_jquery_ui_api_version') ?>/jquery-ui.min.js

Pretty easy right? Notice the – (hyphen) which starts the line, this is a flag to yml parsers that data within current tag will be an array. I found this syntax much easier to deal with vs having several entries on the same line. Also, check out the php echos, they are simply referring to settings we initialized in our app.yml file.

Not only does this solution allow you to serve 3rd party API libraries from 3rd party hosts, you can now upgrade all API libraries with a few minor changes in a centralized location.

Symfony – sfGuardPlugin – Use Email Instead of Username

6

sfGuardPlugin is pretty awesome. It allows a symfony developer the ability to quickly implement a user login and access control system. However, there is an issue with respect to telling the plugin whether to use the username or the email column for validating user submitted input. After some Googling I found a few sites which forced sfGuardPlugin to use email rather than username, but only after quite a bit of work. What I am going to show will accomplish the same end goal, but with minor changes.

First, lets take a look at the calling code that determines how the user submitted login information will be validated.


// plugins/sfDoctrineGuardPlugin/lib/validator/sfGuardValidatorUser.class.php

protected function doClean($values)
{
    $username = isset($values[$this->getOption('username_field')]) ? $values[$this->getOption('username_field')] : '';
    $password = isset($values[$this->getOption('password_field')]) ? $values[$this->getOption('password_field')] : '';

    $allowEmail = sfConfig::get('app_sf_guard_plugin_allow_login_with_email', true);
    $method = $allowEmail ? 'retrieveByUsernameOrEmailAddress' : 'retrieveByUsername';

    // don't allow to sign in with an empty username
    if ($username)
    {
        if ($callable = sfConfig::get('app_sf_guard_plugin_retrieve_by_username_callable'))
        {
            $user = call_user_func_array($callable, array($username));
        } else {
            $user = $this->getTable()->retrieveByUsername($username);
        }
        // user exists?
        if($user)
        {
            // password is ok?
            if ($user->getIsActive() && $user->checkPassword($password))
            {
                return array_merge($values, array('user' => $user));
            }
        }
    }

    if ($this->getOption('throw_global_error'))
    {
        throw new sfValidatorError($this, 'invalid');
    }

    throw new sfValidatorErrorSchema($this, array($this->getOption('username_field') => new sfValidatorError($this, 'invalid')));
}

We are not going to make changes to this code because it’s core code and would be overridden every time you update sfGuardPlugin, rather we are just going to dissect it a bit to see what changes we need to make to another file, and why.

line 08: The code is looking for ‘allow_login_with_email' within our app.yml file, and if it doesn't exist, defaults to true.

line 09: What is the purpose of this line? $method is being set, but is never used. This should be removed in a future iteration.

line 14: Apparently there is the flexibility to define your own callback function by defining retrieve_by_username_callable within our app.yaml file, but if you look at line 16, it forces us to use a function rather than a method.

line 18: Finally, the magic method which determines which field will be used in determining a user’s login information (email or username). But notice the problem here? The code is calling getTable()->retrieveByUsername($username), as far as I know, table classes are supposed to be containers for static methods which help us interface with their instantiated object counterparts. Whatever, we are going to overlook this.

Ok, now for the change YOU need to make to use email rather than username.


// lib/model/doctrine/sfDoctrineGuardPlugin/sfGuardUserTable.class.php

class sfGuardUserTable extends PluginsGuardUserTable
{
    ....

    public function retrieveByUsername($username, $isActive = true)
    {
        $query = self::getInstance()
            ->createQuery('u')
            ->where('u.email_address = ?', $username)
            ->addWhere('u.is_active = ?', $isActive);

        return $query->fetchOne();
    }

    ....
}

Now clear cache, and try to login with an email address instead of an username. With any luck it should work as expected. Sweet right? How about if you want to check both username AND email? No prob, consider the following:

public function retrieveByUsername($username, $isActive = true)
{
    $query = self::getInstance()
        ->createQuery('u')
        ->where('u.email_address = ?', $username)
        ->orWhere(‘u.username = ?’, $username)
        ->addWhere('u.is_active = ?', $isActive);

    return $query->fetchOne();
}

Notice the ‘orWhere’ addition, this will allow sfGuardPlugin to check either the email column OR the username column.

So that’s it, hopefully this seemingly easily feature in theory, however difficult to understand in practice, worked for you.

Symfony – PHP – Possibly Forking 1.x so Invested Companies don’t Lose Millions

14

So there seems to be some rumblings from within the Symfony world that the dev team “may” fork 1.x in order to continue maintenance beyond the announced 2013 “end of life” window, rather than ending active development on 1.x and “hoping” everyone shifts to Symfony 2.

Source blog post @ http://test.ical.ly/2011/06/22/quo-vadis-symfony-1-4/ (lol @ FQDN).

I posted a comment on the blog, which is copied verbatim here in case it gets lost in the static.

It’s amazing that we are even having this type of discussion. We opted to use Symfony back in 0.x for some high traffic projects. Our company spent a lot of time and money training the dev team to correctly use the framework. And it wasn’t just the initial learning of moving from a hodge-podge code-base to MVC code-base, we also had to relearn new ways of doing things with some minor revisions (1.0 -> 1.2 forms for example). How much time was spent learning how to get the form system to work with some highly customized forms? A lot.

And now here we are on 1.4. Content and happy with the throughput the dev team is able to maintain. But now Symonfy says that 2.0 will be released and is completely different than 1.x? How is that fair to the companies, individuals, and hobbyists who have sunk time over the last four years ramping up to a point where we can take advantage of RAD (rapid app development) approach that Symfony provides? Granted, Symfony did extend the maintenance window of 1.x to 2013, but to completely abandon a great framework is callous and irresponsible, to be frank.

When 1.x is no longer supported, the dev team will just have to maintain 1.x as if it were our own. It’s tragic, but financially, we can’t keep paying the costs of heavy refactors incurred with new major revisions. Will we be having this discussion again in three to four years, when Symfony 3 is slated for release? When companies during that time period would have made the brutal choice to shift from Symfony 1 to Symfony 2, only to be abandoned again?

IMO, I think Symfony should follow closer to the examples set by industry giants such as Apache, MySQL, and PHP with regards to major revisions. Make them available, but be gentle about the transition. Leave it up to the companies to determine whether or not to upgrade.

Thanks and I <3 Symfony.

My question is, what do other Symfonians think of the upcoming changes?

Go to Top