Development

sfDoctrineFaq

You must first sign up to be able to contribute.

The sfDoctrine FAQ

sfDoctrine is a model layer replacement plugin for symfony. This plugin is an excellent alternative to Propel.

Why this FAQ ?

This FAQ is designed to help you to discover the sfDoctrine plugin more easily. If you encounter a problem during your developments with sfDoctrine, don't hesitate to check the FAQ.

Feel free to contribute

Share your experience of sfDoctrine with other symfony users ! It's nice to find a solution for a problem without destroying your brain :) ! Think about other users if you find a solution, contribute !

Requested questions

Please insert the questions you need an answer :

  • How can I specify multi column unique key constraint in sfDoctrine? Please see this post for detailed description.
  • Can I mix different inheritance concepts (Column Aggregation, One table per subclass, ...) in a schema ?
  • Is there a way to make a deep copy of an data object ?

FAQ

Installation

How do I install the Doctrine plugin (sfDoctrinePlugin) in symfony 1.1?

See Getting Started with Doctrine (in the Symfony Cookbook).

Troubleshooting

What are the supported Doctrine types in sfDoctrine, and what are their YAML syntaxes ?

All the Doctrine types are supported by sfDoctrine. Here is the Doctrine type documentation. All types are defined with the following syntax:

values with no (or optional and omitted) length parameters

type: typename
#examples:
type: boolean
type: string #note that omitted length params make the column default to the max db size

values with length parameters

type: typename(size)
#examples:
type: string(50)
type: integer(5)

enum has a special syntax:

type: enum
values: [value1, value2, value3]

What is the difference between a relation name and a relation column?

In sfDoctrine there is a clear difference between the name of a relation and it's column. The column contains the id of the foreign record. The column only contains an integer (the key).

Consider the following simple example schema:

User:
  table_name: user
  columns:
    name:
Post:
  table_name: post
  columns:
    user_id: {foreignClass: User, foreignName: Author, localName: Articles}

There is a relation between Posts and Users. The column is user_id but the name is Author.

In the admin generator or when loading data, the difference between name and column is crucial. If you provide the user_id column in the generator.yml file it means that you want to edit the key itself, directly (not recommended). If you provide the Author name in the generator.yml file it means that you want to edit the relation itself (you get a pop-up menu).

When loading the data, this is equally important. The foreign relation is specified using the name of the relation, not the column. Here is an example:

User:
  bob:
    name: Bobby
Post:
  goodOne:
    Author: bob

In the Doctrine queries, it is not recommended either to use the relation column. Use the relation name instead, even if you need to restrict the search via ids only:

sfDoctrine::queryFrom('Post')->where('Post.Author.id = ?', bobsId)

A good rule of thumb is therefore: Never use the relation columns. Not in the generator.yml files, not in the fixture files, not in the Doctrine queries.

Why don't getState or getData don't fetch the records state or data??

This is because you cannot use the get* syntax for some reserved keywords in Doctrine. Those "keywords" are:

  • up
  • NullObect
  • OID
  • ErrorStack
  • State
  • Table
  • Data
  • Modified
  • Prepared
  • Iterator
  • Incremented
  • Last
  • References
  • Related
  • Attribute
  • TableName
  • Inheritance
  • EnumValues
  • Listener
  • Node

Why aren't my foreign keys appearing in the database?

Doctrine's foreign keys are a bit buggy at the moment, and the author has turned the default table output to just tables, with no foreign constraints (unique constraints work, however). If you wish to override this and create tables AND constraints, you can add to your sfDoctrinePlugin doctrine.yml:

export: all

other accepted values: none|constraints|tables (tables is the current doctrine default as described above: all without constraints)

Examples

I really want to override the set and get methods. How do I do that?

Ok, in some cases, it makes sense. Here is the solution:

1. /lib/model/doctrine/YourClass.class.php::

public function set($name, $value, $load = true)
	{
		$method = 'set'.sfInflector::camelize($name);
		if(method_exists($this,$method)) {
			return $this->$method($value);
		}
		return parent::set($name, $value, $load);
	}
	
	
public function get($name, $invoke = true)
	{
		$method = 'get'.sfInflector::camelize($name);
		if(method_exists($this,$method)) {
			return $this->$method();
		}
		return parent::get($name, $invoke);
	}
  
// BAD EXAMPLE (see previous section)
public function setName($v)
	{
		parent::set('stripped_title',myTools::stripText($v));
	  	parent::set('name',$v);
	}

Now, in that bad example, when we call set('name','example name'), we also setting stripped_title.

How to load data with sfDoctrine?

Easiest: Use symfony doctrine-load-data

symfony doctrine-load-data <<app>> <<environment>>

symfony doctrine-load-data myapp prod

Note that this task doesnt create your tables. to do that, use doctrine-insert-sql (or doctrine-build-sql and insert yourself).

Alternative: Use a batch file

Such a php script is much more flexible than a command line since it allows you to choose the application, the environment, the data you want to load, which database connection to use, whether you want to delete the previous entries etc. Here is an example of such a batch script:

<?php

define('SF_ROOT_DIR',    realpath(dirname(__FILE__).'/..'));
define('SF_APP',         'main');
define('SF_ENVIRONMENT', 'dev');
define('SF_DEBUG',       true);

require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');

sfContext::getInstance();
//the below will export your doctrine tables to the db. not necessary if you've already used doctrine-insert-sql!
Doctrine::export('path/to/your/app/lib/model/doctrine/generated');

$data = new sfDoctrineData();
$data->setDeleteCurrentData(true);
$data->loadData(sfConfig::get('sf_data_dir').DIRECTORY_SEPARATOR.'fixtures', 'test');

(note: The 'test' parameter in loadData() is the name of the connection to use, not the name of the yaml file. All .yml files in your fixtures folder will be loaded sequentially.)

When I load data, I see the same class being filled multiple times, what gives ?

sfDoctrine will load data from any .yml file in the target directory. If you have test fixture that you don't want to load, rename the file to something other than .yml and it will be ignored.

How can I create a relation between a new object and an other ?

many solutions are available:

1. Working with object::

$a = new A();
$b = new B();
$a->set('RelationWithB', $b);

$a->save();

2. Working with primary keys::

// You have got B's id.
$idOfB = 256;
$a = new A();
$a->set('b_id', $idOfB);

$a->save();

How can I Implement a One-One relation in my model

It's really easy. You just have to use the 'unique' keyword in your relation option. (unique: true|false) Here's a YAML simple schema file to describe the concept.

A Person hasOne Address An Address hasOne Person

Person:
  tableName: person
  columns:
    firstName: {columnName: first_name, type: string(60)}
    lastName: {columnName: last_name, type: string(100)}
    mail: 
      type: string(150)
      unique: true
      email:  true
    created_at: timestamp
    update_at: timestamp
    address_id:
      type: integer(20) #without setting type you will have error "Unknown field type ''."
      foreignClass: Address
      foreignName: Address
      localName: Person
      cascadeDelete: true
      unique: true

Address:
  tableName: address
  columns:
    numberAndStreet: {columnName: number_and_street, type: string(100)}
    city: {type: string(40)}
    postalCode: {columnName: postal_code, type: string(10)}
    created_at: timestamp
    update_at: timestamp

How can I set up a column aggregation inheritance with my YAML schema file ?

It is really easy to set up a column aggregation inheritance with sfDoctrine using the power of the YAML schema file. The only thing you have to remember, is that one table will contains all the fields related to a parent class and all of its children.

You will understand with this simple example :

Person:
  tableName: person
  columns:
    classKey:  {columnName: class_key, type: integer(3)}
    firstName: {columnName: first_name, type: string(100)}
    lastName:  {columnName: last_name, type: string(100)}

Member:
  inheritance: {extends: Person, keyField: class_key, keyValue: 1}
  columns:
    login: {type: string(20)}
    password: {type: string(32)}

Employee:
  inheritance: {extends: Person, keyField: class_key, keyValue: 2}
  columns:
    employeeSince: {columnName: employee_since, type: timestamp}

Customer:
  inheritance: {extends: Member, keyField: class_key, keyValue: 3}
  columns:
    company: {type: string(60)}

Notice that:

  • You must only set the parent class's tableName, because all the children's fields will be placed in the person table.
  • The keyField is not automatically generated for the parent class.
  • The table used to implement this model in your RDBMS will contain the following fields
    • class_key
    • first_name
    • last_name
    • login
    • password
    • employee_since
    • company

NOTE: the table creation is broken when you do column aggregation inheritance. Temporary fix it to add the child columns to the parent table definition. You can also put off the table creation and create you relational model on your own with your favorite RDBMS manager.

How can I use aggregate functions (max, min, avg, sum ...)

This is quite simple... but I can't find any documentation about it, I hope it'll be usefull for you.

Get the current connection and make a query in DQL syntax :

<?php
  sfDoctrine::connection()->query("
  SELECT MAX(s.version), s.* 
  FROM SomeClass s 
  WHERE s.document_id = ? 
  AND s.culture = ?", 
  array($document_id, $culture));

This query return a Doctrine_Collection object, to get the max version of your collection, just add that :

<?php
  ->getFirst()->max

NOTE : this isn't a mistake, there is no brackets after max

How can I paginate results

A beginners-guide regarding sfDoctrinePager is available at sfDoctrinePager

I'm getting "couldn't locate driver named mysql" when i run a doctrine task or batch from the command line, but doctrine works in browser. What gives?

Odds are that your php.ini file for your CLI (often different from the apache php.ini) doesnt have pdo_mysql enabled. Enable it :)

Why use Doctrine? What are the benefits it brings over Propel, both in general and to symfony users?

The two ORM's are compared at ComparingPropelAndDoctrine.

Using sfFeed2Plugin with Doctrine

Make sure you set generate_accessors: true in your doctrine schema.yml so that autogenerating feed fields from object methods works.

Snippets

All snippets should be created in symfony snippets. Here are the current doctrine snippets.