Archive for category symfony

Extending Propel in Symfony 1.4

Allow Table name prefixed with underscore

Replace the original symfony/lib/plugins/sfPropelPlugin/lib/addon/sfPropelDatabaseSchema.class.php:

  public function getChildren($hash)
  {
    foreach ($hash as $key => $value)
    {
      // ignore special children (starting with _)
      if ($key[0] == '_')
      {
        unset($hash[$key]);
      }
    }

    return $hash;
  }

with:

  public function getChildren($hash)
  {
    foreach ($hash as $key => $value)
    {
      // ignore special children (starting with _)
      if (in_array($key, array('_attributes', '_behaviors', '_propel_behaviors', '_inheritance', '_nestedSet', '_foreignKeys', '_indexes', '_uniques')))
      {
        unset($hash[$key]);
      }
    }

    return $hash;
  }

Now you can define a table name which is prefixed by underscore in your config/schema.yml:

connection:                      propel
defaultIdMethod:                 native
package:                         lib.model

classes:
  MyTable:
    tableName:                   _my_table
    columns:
      id:
      column1:                   { type: varchar, size: 30 }
      column2:                   { type: varchar, size: 255 }

Match the Propel Generated Model Files with Symfony Coding Standard

Propel 1.4 introduce behaviors named alternative_coding_standarts which can be used to alter Propel generated model files according to symfony. But, this behavior known not working and need to be pacthed. Locate symfony/lib/plugins/sfPropelPlugin/lib/vendor/propel-generator/classes/propel/engine/behavior/AlternativeCodingStandardsBehavior.php, and change:

		if($this->getParameter('brackets_newline') == 'true') {
			$filter['#^(/t*)/}/h(else|elseif|catch)(.*)/h/{$#m'] = "$1}
$1$2$3
$1{";
			$filter['#^(/t*)(/w.*)/h/{$#m'] = "$1$2
$1{";
		}

to:

		if($this->getParameter('brackets_newline') == 'true') {
			// class
			$filter['#^(\w.*)\h\{#m'] = "$1\n{";
			// line ending with {
			$filter['#^(\t+)(\w.*)\h\{#m'] = "$1$2\n$1{";
			// } something {
			$filter['#^(\t+)\}\h(else|elseif|catch)(.*)\h\{#m'] = "$1}\n$1$2$3\n$1{";
		}

Change the config/propel.ini to include alternative_coding_standarts behavior as default:

propel.behavior.default                        = symfony,symfony_i18n

to:

propel.behavior.default                        = symfony,symfony_i18n,alternative_coding_standards

And then rebuild your model files.

Adding Behavior to Add __toString() Method for Model Files

To be able to automatically generate __toString() method, symfony_tostring behavior need to be added as default behavior or registered as symfony behaviors in config/schema.yml.

propel.behavior.default                        = symfony,symfony_i18n,symfony_tostring,alternative_coding_standards
propel.behavior.symfony_tostring.class         = plugins.sfPropelPlugin.lib.behavior.SfPropelBehaviorToString

To include a column in __toString() method, define an attribute toString: true to column definition, as in:

classes:
  MyTable:
    tableName:                   _my_table
    columns:
      id:
      column1:                   { type: varchar, size: 30, toString: true }
      column2:                   { type: varchar, size: 255 }

This behavior accept an argument named separator which will be used to concatenate the column if there are many columns defined with toString attribute.

classes:
  MyTable:
    tableName:                   _my_table
    columns:
      id:
      column1:                   { type: varchar, size: 30, toString: true }
      column2:                   { type: varchar, size: 255, toString: true }
    behaviors:
      symfony_tostring:
        separator:               ' - '

Dont forget to rebuild your models.

Download: http://numpang.bkdjombang.com/files/symfony/1.4/extending-propel-in-symfony-1.4.zip

Advertisements

Leave a comment

MySQL Workbench Plugin to Export Schema as Symfony Propel Schema

MySQL Workbench provides DBAs and developers an integrated tools environment for:

  • Database Design & Modeling
  • SQL Development (replacing MySQL Query Browser)
  • Database Administration (replacing MySQL Administrator)

There are numerous MySQL Workbench plugin to export the MySQL schema into Propel, one is found here. This plugin export the schema into Propel xml schema.

For a symfony users, in the Wiki, has a plugin which based on the previous one.

My work extends the symfony one, with added features:

  • Export as Symfony Propel New Schema
  • Allow to sort the result by the tablename
  • Integrated changes from version 0.5 of PropelExport.grt.lua
  • Added support for YEAR field type
  • New 1.05: Added mapping for BLOB, MEDIUMBLOB, and LONGBLOB column
  • New 1.05: Fixed handling of multi columns foreign keys

Download:
Github: https://github.com/tohenk/mysql-workbench-propel-schema
Version 1.05 (Dec 17, 2010): symfony_propel_schema.grt.lua
Version 1.04: symfony_propel_schema_export.grt.lua

, ,

3 Comments

Dynamic Web Service using ckWebServicePlugin

To implement Web Service in symfony, ckWebServicePlugin can offer integration of symfony module as web service. The instruction detail can be found at http://www.symfony-project.org/plugins/ckWebServicePlugin/3_0_0?tab=plugin_readme.

But, to make the plugin installation work, you need additional files:

Then follow the instruction to configure the plugin. But, the readme is rather outdated. For example:

<?php

// apps/frontend/modules/math/actions/actions.class.php
class mathActions extends sfActions
{
  /**
   * An action multiplying two numbers.
   *
   * @ws-enable
   * @ws-method SimpleMultiply
   *
   * @param double $a Factor A
   * @param double $b Factor B
   *
   * @return double The result
   */
  public function executeMultiply($request)
  {
    // nothing changed here...
  }
}

But, it should be:

<?php

// apps/frontend/modules/math/actions/actions.class.php
class mathActions extends sfActions
{
  /**
   * An action multiplying two numbers.
   *
   * @WSMethod(name='SimpleMultiply', webservice='MathApi')
   *
   * @param float $a Factor A
   * @param float $b Factor B
   *
   * @return float The result
   */
  public function executeMultiply($request)
  {
    // nothing changed here...
  }
}

Notice the comment doc tag, it should @WSMethod(name=’SimpleMultiply’, webservice=’MathApi’) instead @ws-method and @ws-enable is removed. You can view the complete module at [project]/plugins/ckWebServicePlugin/test/fixtures/project/apps/tutorial/modules/math/actions/actions.class.php.

By default, the generated WSDL will be located under SF_WEB_DIR, so the wsdl location is at http://localhost/MathApi.wsdl, and the front controller will be http://localhost/MathApi.php. Notice that the front controller defined in the MathApi.wsdl using static address. So if you move your Web Service to production server, you need to alter the front controller manually.

Now the part to make the wsdl front controller dynamically changed. Generate a module named wsdl:

php symfony generate:module frontend wsdl

And change the content of index action to:

<?php

// apps/frontend/modules/wsdl/actions/actions.class.php
class wsdlActions extends sfActions
{
  /**
   * Executes index action
   *
   * @param sfRequest $request A request object
   */
  public function executeIndex(sfWebRequest $request)
  {
    $this->forward404Unless($service = $request->getParameter('service'));
    $wsdl = sprintf('%s/wsdl/%s.wsdl', sfConfig::get('sf_data_dir'), $service);
    if (!file_exists($wsdl))
    {
      $this->forward404('WSDL "'.$wsdl.'" is not found.');
    }

    // generate the wsdl controller based on current request
    $wsdlController = $request->getUriPrefix().$request->getRelativeUrlRoot().'/'.$service.'.php';

    $dom = new DOMDocument();
    if ($dom->load($wsdl))
    {
      // query the soap:address node to change location
      $xpath = new DOMXPath($dom);
      $nodes = $xpath->query('/wsdl:definitions/wsdl:service/wsdl:port/soap:address');
      foreach ($nodes as $node)
      {
        if ($node->hasAttribute('location'))
        {
          $node->setAttribute('location', $wsdlController);
        }
      }

      // output
      $this->response->setContentType($request->getMimeType('xml'));

      return $this->renderText($dom->saveXML());
    }

    throw new sfException('"'.$wsdl.'" is not a valid xml document.');
  }
}

Add a route to your [project]/apps/frontend/config/routing.yml:

wsdl:
  url:   /:service.wsdl
  param: { module: wsdl, action: index }

Move your wsdl from [project]/web/MathApi.wsdl to [project]/data/wsdl/MathApi.wsdl, and last modify your [project]/frontend/config/app.yml, from:

soap:
  ck_web_service_plugin:
    wsdl:                  %SF_WEB_DIR%/MathApi.wsdl

to:

soap:
  ck_web_service_plugin:
    wsdl:                  %SF_DATA_DIR%/wsdl/MathApi.wsdl

If you want to enabled debugging on wsdl front controller, edit your [project]/web/MathApi.php to:

<?php

require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');

$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'soap', true);
sfContext::createInstance($configuration)->dispatch();

, ,

3 Comments

Propel 1.3 Blob Field Workarround on Symfony

VARBINARY or LONGVARBINARY column should be mapped as blob column. But in Propel 1.3 they are mapped wrong. The setter for the column will not work when the column is saved, the column is treated as string instead of resource. The column value will be something like Resource id #nn. See this ticket.

You can apply this patch to symfony to fix this issue.

, ,

Leave a comment

Adding Access Logging to Symfony Application

sfAccessLoggerPlugin introduces a way of logging your site visits. To accomplish this logging, the others plugin is needed:

  1. sfGuardPlugin for User management.
  2. sfPropelUuidBehaviorPlugin for generating Universally Unique Identifier (UUID).
  3. sfRemoteIPPlugin, an extract of function found in sfPropelAuditPlugin by Sacha Telgenhof Oude Koehorst to detect the remote IP Address.
  4. sfBrowscapPlugin, a simple symfony wrapper for phpbrowscap to determine the client browser capability.
  5. The sfAccessLoggerPlugin itself.

Configuration:

  1. Instal all plugins and enable them.
  2. Do propel build model, forms, filters, and SQL, then insert generated SQL to your database.
  3. In your backend application, enable bundled module sfAccessLogViewer to provide admin style generated module to view the access log.

, , , , ,

Leave a comment

Using Multiple Primary Keys in Admin Generator

Sometimes, the table we’re using force us to use multiple primary keys as shown in the schema below (config/schema.yml):

connection:            propel
defaultIdMethod:       native
package:               lib.model

classes:
  State:
    tableName:         state
    columns:
      id:              { type: varchar, size: 2, primaryKey: true }
      country_id:      { type: varchar, size: 2, primaryKey: true }
      name:            { type: varchar, size: 50, index: true }
      created_at:

By default, the symfony Admin Generator will only support single primary key. Since symfony 1.2, which adds sfRoute class, using multiple primary keys is possible by a configuration modifications and adds a few lines of code.

So now, generate the admin module by issuing:

symfony propel:generate-admin frontend State

Look at the generated route in apps/frontend/config/routing.yml:

state:
  class: sfPropelRouteCollection
  options:
    model:                State
    module:               state
    prefix_path:          state
    column:               id
    with_wildcard_routes: true

Only a single primary key was generated, so the idea is concatenate the primary keys as a single column, separated by a dash (-) for example.

Modify the route:

state:
  class: sfPropelRouteCollection
  options:
    model:                State
    module:               state
    prefix_path:          state
    column:               countryandstate
    with_wildcard_routes: true
    model_methods:
      object:             doSelectForRoute
  requirements:           { countryandstate: '[a-zA-Z0-9\.\_\-\:]+' }

As seen in the modified route, we change the column name to countryandstate, add a model-methods for object configuration and change the countryandstate column requirements.

When an object converted as a route, the sfRoute class will check for toParams() method, so we can provide the value of countryandstate column by adding this code:

// lib/model/State.php
<?php

class State extends BaseState
{
  public function toParams()
  {
    return array('countryandstate' => implode('-', array($this->getCountryId(), $this->getId())));
  }
}

A model-methods for object configuration in the routing above, changes the method used by the sfRoute class to retrieve an object which match a route.

// lib/model/StatePeer.php
<?php

class StatePeer extends BaseStatePeer
{
  /**
   * Retrieve State object for routing.
   *
   * @param array $parameters  The route parameters
   * @return State
   */
  public static function doSelectForRoute($parameters)
  {
    if (!isset($parameters['countryandstate']))
    {
      return null;
    }

    $c = new Criteria();
    list($country, $state) = explode('-', $parameters['countryandstate']);
    $c->add(StatePeer::COUNTRY_ID, $country)
      ->add(StatePeer::ID, $state);

    return StatePeer::doSelectOne($c);
  }
}

The rest is, to customize the generated module and adjusting the form as you need.

, , ,

13 Comments

Hacking Symfony Application in A Shared Hosting

In a shared hosting environment, you may not have a full access to the server. Everything has already been installed. But a least they provide a secure shell (SSH).

I have successfully deploy a syfmony 1.2 application in a shared hosting, surely with a hacking.

PDO Hack

Symfony 1.2 at least need PHP 5.2.4 and above. So if your PHP version is 5.2.0 and you’re using MySQL database, make sure to activate PDO::MYSQL_ATTR_USE_BUFFERED_QUERY for buffered MYSQL Api.

Edit your databases.yml:

dev:
  propel:
    param:
      classname:  DebugPDO

all:
  propel:
    class:        sfPropelDatabase
    param:
      dsn:        mysql:dbname=your_db;host=localhost
      username:   root
      password:   your_password
      encoding:   utf8
      persistent: true
      pooling:    true
      classname:  PropelPDO
      options:
        MYSQL_ATTR_USE_BUFFERED_QUERY:  {value: on}

Symfony CLI Hack

Usually, we can customize the php.ini file for web server, but not for the PHP CLI. Unfortunately, the PDO extension for PHP CLI is not enabled by default. As in shared hosting environment, there are no write access to system configuration.

Try this:

$ php -i | grep PDO

If you got an output telling about PDO extensions has been enabled then, you don’t need this hack.

First, we can create our own php.ini file to be passed to the PHP CLI, and put in a writable directory, for example in the home directory:

# ~/php.ini
extension=pdo.so
extension=pdo_mysql.so
memory_limit=64M

Wrap the symfony command to make sure PHP CLI is using our custom php.ini, create the script and name it symfony.sh. Put in home directory for ease the access:

# ~/symfony.sh
cd /path/to/myproject
php -c ~/php.ini -f symfony -- $*

Make sure the script is executable:

$ chmod u+x ~/symfony.sh

Now you can launch the symfony CLI from within your home directory by issuing:

$ ./symfony.sh [command]

, , , , ,

6 Comments