Category Archives: Doctrine

up and down arrow

Symfony 1.4 embed ordered Doctrine relations

Imagine a scenario where a “Product has N ordered Photos”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Product:
  columns
:
    name
:              { type: string(128), notnull: true }
 
Photo
:
  columns
:
    url
:         { type: string(255) }
    product_id
:  { type: integer }
    position
:    { type: integer }
  relations
:
    Product
:
      local
:           product_id
      foreign
:         id
      foreignAlias
:    Photos
      onDelete
:        CASCADE

In the ProductForm you can embed the related Photos with:

1
2
3
4
5
6
7
class ProductForm extends BaseProductForm
{
    public function configure()
    {
        $this->embedRelation('Photos')
    }
}

Problem: The related Photos will be displayed ordered by their id

Solution: Specify the relation between Product and Photos on the schema.yml file and provide a orderBy attribute:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Product:
  columns
:
    name
:              { type: string(128), notnull: true }
  relations
:
    Photos
:
      type
:     many
      class
:    Photo
      local
:    id
      foreign
:  product_id
      orderBy
:  position ASC   # this is the magic part!
      onDelete
: CASCADE
 
Photo
:
  columns
:
    url
:         { type: string(255) }
    product_id
:  { type: integer }
    position
:    { type: integer }
  relations
:
    Product
:
      local
:           product_id
      foreign
:         id
      foreignAlias
:    Photos
      onDelete
:        CASCADE

Example adapted from: http://devblog.lexik.fr/symfony/tips-symfony-1-31-4-orderby-des-relations-1026

Symfony1.4 and Doctrine1.2 setting and getting child elements in execution time

Question

I’m using Symfony1.4 and Doctrine1.2 to create children objects and print them in execution time. The problem is that I always get the children objects from the first query.

I have the following schema:

1
2
3
4
5
6
7
8
Parent:
  id
  name

Child:
  id
  parent_id
  name

My operations:

  1. Initially I have one Parent (with id=1), and no Child
  2. I grab a Parent (id=1) and store that object on $parent
  3. List it’s Child objects
  4. Result: none OK: as expected
  5. Create a new Child and set it’s parent to 1
  6. List $parent‘s Child objects
  7. Result: none OOPS: I expected the new Child from 5.!

Code:

1
2
3
4
5
6
$parent = Doctrine_Query::create()->from('Parent p')->where('p.id = ?', 1)->limit(1)->fetchOne(); /* from 2. */
print_r($parent->getChild()->toArray()); /* from 3. */
$child = new Child();
$child->setParentId($parent->getId());
$child->save(); /* from 4. */
print_r($parent->getChild()->toArray()); /* from 6. */

Note, from Doctrine’s comments:

1
2
3
4
5
6
7
8
9
10
11
/**
* refresh
* refresh internal data from the database
*
* @param bool $deep                        If true, fetch also current relations. Caution: this deletes
*                                          any aggregated values you may have queried beforee
*
* @throws Doctrine_Record_Exception        When the refresh operation fails (when the database row
*                                          this record represents does not exist anymore)
* @return boolean
*/

I’ve already tried re-grabbing the $parent before the last line, but the result is the same.

Own Answer

I found a work-around for this issue:

1
2
3
4
5
6
7
$parent = Doctrine_Query::create()->from('Parent p')->where('p.id = ?', 1)->limit(1)->fetchOne();
print_r($parent->getChild()->toArray());
$child = new Child();
$child->setParentId($parent->getId());
$child->save();
$parent->refresh(true); /* see note! */
print_r($parent->getChild()->toArray());

http://stackoverflow.com/questions/18859509/symfony1-4-and-doctrine1-2-setting-and-getting-child-elements-in-execution-time

Como evitar “The “default” context does not exist.” quando se corre tasks do doctrine em symfony 1.4

O erro acontece devido ao uso do sfContext::getInstance() no código.

Temos duas formas de possível correcção:

Ou se edita a task em questão e se coloca

1
sfContext::createInstance($this->configuration)

para criar um context.

Ou se cria o contexto no código:

1
2
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'doctrine', false);
$context = sfContext::createInstance($configuration);
optimization-icon

Doctrine 1.2 (Symfony 1.4) performance tips & tricks

Sure Doctrine is awesome, but sometimes (every time?), it is also a memory killer. Hopefully there are some handy tricks that can be used on several occasions. Here are some Doctrine 1.2 performance tips & tricks I’ve collected over the time working with Symfony 1.4. Hope they’re useful to you guys!

If you know more performance tips, please leave them in the comments.

 

 

Tip #1

Create a task environment to run tasks and disable the profiler on databases.yml:

1
2
3
4
task:
  doctrine
:
    param
:
      profiler
: false

 

Tip #2

Enable Doctrine’s auto free query objects

1
Doctrine_Manager::connection()->setAttribute(Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS, true);

 

Tip #3

Free Doctrine objects when they are no longer needed

1
2
$doctrine_object->free(true);
$doctrine_object = null;

 

Tip #4

Force Doctrine connection clear after performing needed Doctrine operations

1
2
3
4
5
6
7
Doctrine_Manager::connection()->connect();

// code code ...
$doctrine_object = Doctrine_Query::create()->from('Modal a')->limit(1)->fetchOne();
// code code...

Doctrine_Manager::connection()->clear();

 

Tip #5

Hydrate array when Model objects are not needed.

1
$doctrine_object = Doctrine_Query::create()->from('Modal a')->limit(1)->execute(array(), Doctrine_Core::HYDRATE_ARRAY);

 

Tip #6

Enable APC caching on Doctrine queries.

1
2
Doctrine_Manager::connection()->setAttribute(Doctrine::ATTR_QUERY_CACHE, new Doctrine_Cache_Apc(array('prefix' => 'fancyapcprefix_')));
Doctrine_Manager::connection()->setAttribute(Doctrine_Core::ATTR_RESULT_CACHE, new Doctrine_Cache_Apc(array('prefix' => 'fancyapcprefix_')));

 

Tip #7

Perform bulk inserts instead of one-by-one inserts.

1
2
3
4
5
6
7
8
9
10
11
$record1 = new Model()
$record1->setName('Example #1');
$record2 = new Model()
$record2->setName('Example #2');

$col = new Doctrine_Collection('Model');
$col->add($record1);
$col->add($record2);
$col->add($record3);
$col->add($recordN);
$col->save();

from this post

 

You can read more on Doctrine 1.2 performance here, here, here and here.

Debugging doctrine:data-load

I know it drives you MAD when you try to load a bunch of features and get an error like:

1
SQLSTATE[23000]: Integrity CONSTRAINT violation: 1062 Duplicate entry '16' FOR KEY 'PRIMARY'

Luckily there’s an undocumented argument on doctrine’s data-load task:

–trace

1
2
3
4
5
6
7
8
9
10
$ php symfony doctrine:data-load --trace

>> doctrine Loading data fixtures from "/home/bla/public_html/site/data/fixtures"
>> doctrine Loading data fixtures from "/home/bla/public_html/site/plugins/sfDoctrineGuardPlugin/data/fixtures"
>> Doctrine_Connection_Mysql exec : SET NAMES 'UTF8' - ()
>> Doctrine_Connection_Mysql exec : DELETE FROM table_a - ()
>> Doctrine_Connection_Mysql exec : DELETE FROM table_b - ()
>> Doctrine_Connection_Mysql exec : DELETE FROM sf_guard_user - ()
>> Doctrine_Connection_Statement execute : INSERT INTO sf_guard_user (algorithm, is_active, is_super_admin, first_name, last_name, email_address, username, salt, password, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - (sha1, 1, 1, bla, blah, blah@adclick.pt, blah, blah48ca1e4247258a0ec29f9d1aac8, blah32ed46c3a74deebb071456114e4406eba42e, 2012-01-12 18:07:02, 2012-01-12 18:07:02)
...

Criar schema.yml a partir do workbench

Para quem utiliza a ferramenta Workbench para desenhar os modelos da base de dados, e depois não quer ter o trabalho de passar os nomes todos para o schema.yml, pode tirar partido desta script.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?php
/*
 *  The MIT License
 *
 *  Copyright (c) 2010 Johannes Mueller <circus2(at)web.de>
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */


// show errors
error_reporting(E_ALL);

// lets stop the time
$start = microtime(true);


// enable autoloading of classes
require_once('../lib/MwbExporter/Core/SplClassLoader.php');
$classLoader = new SplClassLoader();
$classLoader->setIncludePath('../lib');
$classLoader->register();

// show a simple text box with the output
echo '<textarea cols="100" rows="50">';

    // configure your output
    $setup = array(
        'extendTableNameWithSchemaName' => true
    );

    // create a formatter
    $formatter = new \MwbExporter\Formatter\Doctrine1\Yaml\Loader($setup);
   
    // parse the mwb file
    $mwb = new \MwbExporter\Core\Workbench\Document('data/er.mwb', $formatter);
   
    // show the export output of the mwb file
    echo $mwb->display();
 
echo "</textarea>";

// save as zip file in current directory and use .yml as file endings
echo "<br><br>";
echo $mwb->zipExport(__DIR__, 'yml');

// show some information about used memory
echo "<br><br>";
echo (memory_get_peak_usage(true) / 1024 / 1024) . " MB used";
echo "<br>";

// show the time needed to parse the mwb file
$end = microtime(true);
echo  sprintf('%0.3f', $end-$start) . " sec needed";

Atenção: Não dispensa verificação do código gerado!!!

Links úteis:
Workbench: http://www.mysql.com/downloads/workbench/
Repositório do projecto: git://github.com/johmue/mysql-workbench-schema-exporter.git

Obrigado ao johmue. :P

Apc para o Doctrine em projectos symfony

Em projectos symfony 1.*, pode ser configurado o uso da cache Apc para o Doctrine, para tal, em config/ProjectConfiguration.class.php, criar o método:

public function configureDoctrine(Doctrine_Manager $manager)
{
$manager->setAttribute(Doctrine::ATTR_QUERY_CACHE, new Doctrine_Cache_Apc(array(‘prefix’ => ‘PREFIXO’)));
}

É importante definir um prefixo, já que tendo vários projectos na máquina, usando a mesma chave, gera conflito.

Doctrine use same collation on all tables (even plugins)

It really annoys me when I set my schema collate to utf8_general_ci but plugin tables are created as latin1_swedish_ci.

Solution?

Create the following method on your ProjectConfiguration.class.php

1
2
3
4
5
6
7
8
9
class ProjectConfiguration extends sfProjectConfiguration
{
    public function configureDoctrine(Doctrine_Manager $manager)
    {
        $manager->setCollate('utf8_general_ci');
        $manager->setCharset('utf8');

    }
}

Props to: http://www.prettyscripts.com/framework/symfony/symfony-and-doctrine-default-table-collation-a-better-solution