Search This Blog

Wednesday, January 16, 2013

More on Symfony 2 - Entities and Data Fixtures

Entities and Data Fixtures in Symfony 2.

Entities (Doctrine).

I discovered the use of yml schema files with Symfony 1.4. Symfony 2 seems to favour using annotations in php classes. I understand the attraction of keeping everything together in the one file, but I prefer seperate yml files.
Also with v1.4 I have been using ORM Designer. ORM Designer is a great way to design and maintain a schema which can them be used by the framework to build the classes to access it.
A 3 class schema - with
Product Ranges --> Products --> Product Options in yml looks like this:

Product Range:

Test\WebsiteBundle\Entity\ProductRange:
    repositoryClass: Test\WebsiteBundle\Entity\ProductRangeRepository
    type: entity
    table: null
    fields:
      id:
        type: integer
        id: true
        generator:
          strategy: AUTO
      name:
        type: text
        length: 32
      slug:
        type: text
        length: 32
      title:
        type: text
        length: 256
      shortDescription:
        type: text
        length: 512
      description:
        type: text
        length: 2048
    oneToMany:
      products:
          targetEntity: Product
          mappedBy: productRange 
Products:
Test\WebsiteBundle\Entity\Product:
    repositoryClass: Test\WebsiteBundle\Entity\ProductRepository
    type: entity
    table: null
    fields:
      id:
        type: integer
        id: true
        generator:
          strategy: AUTO
      name:
        type: text
        length: 32
      slug:
        type: text
        length: 32
      title:
        type: text
        length: 256
      shortDescription:
        type: text
        length: 512
      description:
        type: text
        length: 2048
      image:
        type: text
        length: 256
    manyToOne:
      productRange:
        targetEntity: ProductRange
        inversedBy: productRange
        joinColumn:
          name: productRange_id
          referencedColumnName: id
    oneToMany:
      productImage:
          targetEntity: ProductImage
          mappedBy: product 

Product Images:
Test\WebsiteBundle\Entity\ProductImage:
    type: entity
    table: null
    repositoryClass: Test\WebsiteBundle\Entity\ProductImageRepository
    fields:
      id:
        type: integer
        id: true
        generator:
          strategy: AUTO
      caption:
        type: text
        length: 256
      imagePath:
        type: text
        length: 256
manyToOne: product: targetEntity: Product inversedBy: product joinColumn: name: product_id referencedColumnName: id
Notes:
  • Doctrine reference is here
  • Repository classes (which are optional) are specified for each of the 3 classes.
  • 2 One to many relations: Product Range --> Product and Product --> ProductImage are specified.
  • The inverse (Many to one) relationships: --> Product from ProductRange and --> ProductImage from Product) are specified. 
  • These relationships are bidirectional. In ManytoOne/OnetoMany bidirectional relationships the 'many' side (Product) is the owner; so in ProductRange --> Product, Product is the owning site (ProductRange is the inverse).
  • The owning side is where the updates (inserts/deletes) take place.
  • The owning side of a relationship contains the foreign key, and specifies the 'inversedBy' attribute. The inverse side specifies the 'mappedBy' attribute.
The relationships above are more verbose than they need to be, the join column and join table definitions have sensible values. The Product --> ProductImage relationship could be rewritten as:

    manyToOne:
      product:
        targetEntity: Product
        inversedBy: product

Data Fixtures.

Another feature missing from Symfony 2.4 are data fixtures - typically yml files containing development/test or live data for an application.


Note relations are needed (for any complex fixtures)  to be working, see Relations below.

To use data fixtures, Khepin has a great bundle at:

(git://github.com/khepin/KhepinYamlFixturesBundle.git)

Install this as per the readme,
copy bundle to src/

   mkdir src/Khepin

   cp –p ../KhepinYamlFixturesBundle
src/Khepin

   mv src/Khepin/KhepinYamlFixturesBundle
src/Khepin/YamlFixturesBundle
add reference to config/AppKernel.php
$bundles[] = new Khepin\YamlFixturesBundle\KhepinYamlFixturesBundle();


3.     add reference to app/config/config.yml to your bundle:
khepin_yaml_fixtures:
  resources:
    - TestWebsiteBundle

 Put fixtures file under <bundle-name>/Datafixtures  - with one fixture per class.
To load:
app/console khepin:yamlfixtures:load


Fixtures should be named in the order they should be loaded - use a numerical prefix.
This script can be used to load data (and optionaly regenereate the schema and models):
#!/bin/sh
umask 0000
if [ "$1" != 'dbonly' ]; then
  app/console doctrine:generate:entities ACOCUSA --path=src/
fi
app/console doctrine:schema:drop --force
app/console doctrine:schema:create
app/console khepin:yamlfixtures:load

Note the setting of umask to 0000 - running this will update the cache files and the site will fail - when accessed through the httpd server (running as a different user - apache).

Example Fixtures:

The khepin yaml fixtures look like the examples below, the file names are ordered:
10_productRange.yml:
model: Test\WebsiteBundle\Entity\ProductRange
persistence: orm
fixtures:
  klassikdrain:
    title: Klassik Drain
    name: klassikdrain
    slug: klassik
    shortDescription: The world’s most widely used modular trench drain.
    description: The world’s most widely used modular trench drain is an interconnecting, general purpose 4” internal width drain system for both internal or external use. The system features an aesthetic, integrally cast-in, steel wearing edge. A wide range of grates in different styles are secured into the channel unit by the boltless locking “QuickLok” system.
  powerdrain:
    title: Power Drain
    name: powerdrain
    slug: power
    shortDescription: Heavy duty sloped trench drain system ideal for applications requiring the most rugged product.
    description: Heavy duty sloped trench drain system ideal for applications requiring the most rugged product. PowerDrain is available in both 4” (S100K) and 12” (S300K) wide systems and features the patented PowerLok boltless locking system to secure grates. The interconnecting channels feature integrally cast-in ductile iron edge rails.


15_product.yml
model: Test\WebsiteBundle\Entity\Product
persistence: orm
fixtures:
  k100:
    title: K100
    name: K100
    slug: k100
    productRange: klassikdrain
    shortDescription: KlassikDrain - K100 features a 4'' internal width.
    description: KlassikDrain - K100 features a 4'' internal width and a wide choice of grates - from decorative to ADA-compliant and Load Class E - for use in applications from parking lot drainage to pedestrian-crowded shopping malls.
    
  k200:
    title: K200
    name: K200
    slug: k200
    productRange: klassikdrain
    shortDescription: KlassikDrain - K200 features a 8'' internal width.
    description: KlassikDrain - K200 features a 8'' internal width and a wide choice of grates - from decorative to ADA-compliant and Load Class E - for use in applications from parking lot drainage to pedestrian-crowded shopping malls.
    
  k300:
    title: K300
    name: K300
    slug: k300
    productRange: klassikdrain
    shortDescription: KlassikDrain - K300 features a 12'' internal width.
    description: KlassikDrain - K300 features a 12'' internal width and a wide choice of grates - from decorative to ADA-compliant and Load Class E - for use in applications from parking lot drainage to pedestrian-crowded shopping malls.    

20_productImage.yml
model: Test\WebsiteBundle\Entity\ProductImage
persistence: orm
fixtures:
  productimage_1:
    caption: Perforated Steel
    product: k100
    imagePath: grates/perforated-steel.gif

  productimage_2:
    caption: ADA Plastic
    product: k200
    imagePath: grates/ada-plastic-grate.gif

  productimage_3:
    caption: ADA Stainless
    product: k300
    imagePath: grates/ada-stainless-grate.gif

Generating Fixture Data files from CSV (Spreadsheet Data).

Source data is commonly provided in the form of spreadsheets.
There are a number of python scripts to convert csv (and other formats) to yml, the script below is a php script to do this, it uses the pecl yaml package: http://pecl.php.net/package/yaml, which requires LibYAML
The scrip reads a source file (test.yml), it uses fgetscsv to read each file into an array (the first line is assumed to contain column headers and is read into a header array).
Each item in the output file is saved as an array in the results array, this array is then converted to a yml string using yaml_emit which is then written to the output file. As a hack an additional column may be written - displayOrder which will be set to the row number of each item in the source file.

/  $csv = "products.csv";
$yaml = "15_product.yml";
   $model='Test\WebsiteBundle\Entity\PartsList';

echo "csv to yaml $csv ==> $yaml\n";
if (!$fhcsv=fopen($csv,"r")) {
echo "error opening $csv\n";
exit(1);
}
if (!$fhyaml=fopen($yaml,"w")) {
echo "error opening $yaml\n";
exit(1);
}
$headers=fgetcsv($fhcsv);
$i=1;
$result=array();
$result['model']=$model;
$result['persistence']='orm';

$fixtures=array();
while($data=fgetcsv($fhcsv)){
  $item='k100_'.$i;
  $thisrow=array();
  foreach($headers as $colid => $column) $thisrow[$column] = $data[$colid];
  if ($addDisplayOrder) $thisrow['displayOrder'] = $i;
  $fixtures[$item] = $thisrow;
  $i++;
}
fclose($fhcsv);
$result['fixtures']=$fixtures;

f  puts($fhyaml, yaml_emit($result));
fclose($fhyaml);




No comments:

Post a Comment