Using Multiple Primary Keys in Admin Generator

Posted on April 22, 2009

13



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.

About these ads
Posted in: symfony