Working with Symfony2 - Introduction

    If you are interested to work with Propel2 with Symfony2, you should consider using the PropelBundle.

    Installation

    The recommended way to install this bundle is to rely on Composer:

    {
        "require": {
            // ...
            "propel/propel-bundle": "1.2.*"
        }
    }
    

    Tip
    Different bundle version may be needed for different Symfony2 version. Check PropelBundle for details.

    Alternatively, you can use Git, SVN, Git submodules, or the Symfony vendor management (deps file):

    Clone this bundle in the vendor/bundles/Propel directory:

    > git submodule add https://github.com/propelorm/PropelBundle.git vendor/bundles/Propel/PropelBundle
    

    Checkout Propel and Phing in the vendor directory:

    > svn checkout http://svn.github.com/propelorm/Propel.git vendor/propel
    
    > svn checkout http://svn.phing.info/tags/2.4.6/ vendor/phing
    

    Instead of using svn, you can clone the unofficial Git repositories:

    > git submodule add https://github.com/phingofficial/phing.git vendor/phing
    
    > git submodule add https://github.com/propelorm/Propel.git vendor/propel
    

    Instead of doing this manually, you can use the Symfony vendor management via the deps file. If you are using a Symfony2 2.x.x version (actually, a version which is not 2.1 or above), be sure to deps.lock the PropelBundle to a commit on the 2.0 branch, which does not use the Bridge

    The second step is to register this bundle in the AppKernel class:

    <?php
    public function registerBundles()
    {
        $bundles = array(
            // ...
            new Propel\PropelBundle\PropelBundle(),
        );
    
        // ...
    }
    

    Don't forget to register the PropelBundle namespace in app/autoload.php if you are not using Composer:

    <?php
    $loader->registerNamespaces(array(
        // ...
        'Propel' => __DIR__.'/../vendor/bundles',
    ));
    $loader->registerPrefixes(array(
        // ...
        'Phing'  => __DIR__.'/../vendor/phing/classes/phing',
    ));
    

    You are almost ready, the next steps are:

    Now, you can build your model classes, and SQL by running the following command:

    > php app/console propel:build [--classes] [--sql] [--insert-sql]
    

    To insert SQL statements, use the propel:sql:insert command:

    > php app/console propel:sql:insert [--force]
    

    Note that the --force option is needed to actually execute the SQL statements.

    Congratulations! You're done; just use the Model classes as any other class in Symfony2:

    <?php
    
    class HelloController extends Controller
    {
        public function indexAction($name)
        {
            $author = new \Acme\DemoBundle\Model\Author();
            $author->setFirstName($name);
            $author->save();
    
            return $this->render('AcmeDemoBundle:Hello:index.html.twig', array(
                'name' => $name, 'author' => $author)
            );
        }
    }
    

    Bundle Inheritance

    The PropelBundle makes use of the bundle inheritance. Currently only schema inheritance is provided.

    Schema Inheritance

    You can override the defined schema of a bundle from within its child bundle. To make use of the inheritance you only need to drop a schema file in the Resources/config folder of the child bundle.

    Each file can be overridden without interfering with other schema files. If you want to remove parts of a schema, you only need to add an empty schema file.

    Configuration

    Symfony configuration

    In order to use Propel, you have to configure few parameters in your app/config/config.yml file.

    If you are not using Composer, add this configuration:

    # in app/config/config.yml
    propel:
        path:       "%kernel.root_dir%/../vendor/propel"
        phing_path: "%kernel.root_dir%/../vendor/phing"
    

    Now, you can configure your application.

    Basic Configuration

    If you have just one database connection, your configuration will look like as following:

    # app/config/config*.yml
    propel:
        dbal:
            driver:               mysql
            user:                 root
            password:             null
            dsn:                  mysql:host=localhost;dbname=test;charset=UTF8
            options:              {}
            attributes:           {}
    

    The recommended way to fill in these information is to use parameters:

    # app/config/config*.yml
    # define the parameters in app/config/parameters.yml
    propel:
        dbal:
            driver:               %database_driver%
            user:                 %database_user%
            password:             %database_password%
            dsn:                  %database_driver%:host=%database_host%;dbname=%database_name%;charset=UTF8
            options:              {}
            attributes:           {}
    

    Configure Multiple Connection

    If you have more than one connection, or want to use a named connection, the configuration will look like:

    # app/config/config*.yml
    propel:
        dbal:
            default_connection:         conn1
            connections:
                conn1:
                    driver:             mysql
                    user:               root
                    password:           null
                    dsn:                mysql:host=localhost;dbname=db1
                conn2:
                    driver:             mysql
                    user:               root
                    password:           null
                    dsn:                mysql:host=localhost;dbname=db2
    

    Configure Master/Slaves

    You can also configure Master/Slaves:

    # app/config/config*.yml
    propel:
        dbal:
            default_connection:         default
            connections:
                default:
                    driver:             mysql
                    user:               root
                    password:           null
                    dsn:                mysql:host=localhost;dbname=master
                    slaves:
                        slave_1:
                            user:       root
                            password:   null
                            dsn:        mysql:host=localhost;dbname=slave_1
    

    Attributes, Options, Settings

    # app/config/config*.yml
    propel:
        dbal:
            default_connection:         default
            connections:
                default:
                    # ...
                    options:
                        ATTR_PERSISTENT: false
                    attributes:
                        ATTR_EMULATE_PREPARES: true
                    settings:
                        charset:        { value: UTF8 }
                        queries:        { query: 'INSERT INTO BAR ('hey', 'there')' }
    

    options, attributes and settings are parts of the runtime configuration. See Runtime Configuration File documentation for more explanation.

    Logging

    You can disable the logging by changing the logging parameter value:

    # in app/config/config.yml
    propel:
        logging:    %kernel.debug%
    

    Propel Configuration

    You can add a app/config/propel.ini file in your project to specify some configuration parameters. See the Build properties Reference to get more information. However, the recommended way to configure Propel is to rely on build properties, see the section below.

    By default the PropelBundle is configured with the default parameters:

    # Enable full use of the DateTime class.
    # Setting this to true means that getter methods for date/time/timestamp
    # columns will return a DateTime object when the default format is empty.
    propel.useDateTimeClass = true
    
    # Specify a custom DateTime subclass that you wish to have Propel use
    # for temporal values.
    propel.dateTimeClass = DateTime
    
    # These are the default formats that will be used when fetching values from
    # temporal columns in Propel. You can always specify these when calling the
    # methods directly, but for methods like getByName() it is nice to change
    # the defaults.
    # To have these methods return DateTime objects instead, you should set these
    # to empty values
    propel.defaultTimeStampFormat =
    propel.defaultTimeFormat =
    propel.defaultDateFormat =
    
    # A better Pluralizer
    propel.builder.pluralizer.class = builder.util.StandardEnglishPluralizer
    

    Build properties

    You can define build properties by creating a propel.ini file in app/config like below, but you can also follow the Symfony2 convention by adding build properties in app/config/config.yml:

    # app/config/config.yml
    propel:
        build_properties:
            xxxxx.xxxx.xxxxx:   XXXX
            xxxxx.xxxx.xxxxx:   XXXX
            // ...
    

    Behaviors

    You can register Propel behaviors using the following syntax:

    # app/config/config.yml
    propel:
        behaviors:
            behavior_name: My\Bundle\Behavior\BehaviorClassName
    

    If you rely on third party behaviors, most of them are autoloaded so you don't need to register them. But, for your own behaviors, you can either configure the autoloader to autoload them, or register them in this section (this is the recommended way when you namespace your behaviors).

    XML Schema

    Place the following schema in src/Acme/DemoBundle/Resources/config/schema.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <database name="default" namespace="Acme\DemoBundle\Model" defaultIdMethod="native">
    
        <table name="book">
            <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
            <column name="title" type="varchar" primaryString="1" size="100" />
            <column name="ISBN" type="varchar" size="20" />
            <column name="author_id" type="integer" />
            <foreign-key foreignTable="author">
                <reference local="author_id" foreign="id" />
            </foreign-key>
        </table>
    
        <table name="author">
            <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
            <column name="first_name" type="varchar" size="100" />
            <column name="last_name" type="varchar" size="100" />
        </table>
    
    </database>
    

    If you are working with an existing database, please check the related section.

    The Commands

    The PropelBundle provides a lot of commands to manage migrations, database/table manipulations, and so on.

    Database Manipulations

    You can create a database:

    > php app/console propel:database:create [--connection[=""]]
    

    As usual, --connection allows you to specify a connection.

    You can drop a database:

    > php app/console propel:database:drop [--connection[=""]] [--force]
    

    As usual, --connection allows you to specify a connection.

    Note that the --force option is needed to actually execute the SQL statements.

    Form Types

    You can generate stub classes based on your schema.xml in a given bundle:

    > php app/console propel:form:generate [-f|--force] bundle [models1] ... [modelsN]
    

    It will write Form Type classes in src/YourVendor/YourBundle/Form/Type.

    You can choose which Form Type to build by specifing Model names:

    > php app/console propel:form:generate @AcmeDemoBundle Book Author
    

    Graphviz

    You can generate Graphviz file for your project by using the following command line:

    > php app/console propel:graphviz:generate
    

    It will write files in app/propel/graph/.

    Migrations

    Generates SQL diff between the XML schemas and the current database structure:

    > php app/console propel:migration:generate-diff
    

    Executes the migrations:

    > php app/console propel:migration:migrate
    

    Executes the next migration up:

    > php app/console propel:migration:migrate --up
    

    Executes the previous migration down:

    > php app/console propel:migration:migrate --down
    

    Lists the migrations yet to be executed:

    > php app/console propel:migration:status
    

    Table Manipulations

    You can drop one or several tables:

    > php app/console propel:table:drop [--force] [--connection[="..."]] [table1] ... [tableN]
    

    As usual, --connection allows to specify a connection.

    The table arguments define which table will be delete, by default all table.

    Note that the --force option is needed to actually execute the deletion.

    Working with existing databases

    Run the following command to generate an XML schema from your default database:

    > php app/console propel:reverse
    

    You can define which connection to use:

    > php app/console propel:reverse --connection=default
    

    This will create your schema file under app/propel/generated-schemas. You need to move/copy it to the corresponding bundle config directory. For example: src/Acme/DemoBundle/Resources/config/.

    The Fixtures

    Fixtures are data you usually write to populate your database during the development, or static content like menus, labels, ... you need by default in your database in production.

    Loading Fixtures

    The following command is designed to load fixtures:

    > php app/console propel:fixtures:load [-d|--dir[="..."]] [--xml] [--sql] [--yml] [--connection[="..."]] [bundle]
    

    As you can see, there are many options to allow you to easily load fixtures.

    As usual, --connection allows to specify a connection. The --dir option allows to specify a directory containing the fixtures (default is: app/propel/fixtures/). Note that the --dir expects a relative path from the root dir (which is app/).

    The --xml parameter allows you to load only XML fixtures. The --sql parameter allows you to load only SQL fixtures. The --yml parameter allows you to load only YAML fixtures.

    You can mix --xml, --yml and --sql parameters to load XML, YAML and SQL fixtures at the same time. If none of this parameter are set all files YAML, XML and SQL in the directory will be load.

    You can pass a bundle name to load fixtures from it. A bundle's name starts with @ like @AcmeDemoBundle.

    > php app/console propel:fixtures:load @AcmeDemoBundle
    

    XML Fixtures

    A valid XML fixtures file is:

    <Fixtures>
        <Object Namespace="Awesome">
            <o1 Title="My title" MyFoo="bar" />
        </Object>
        <Related Namespace="Awesome">
            <r1 ObjectId="o1" Description="Hello world !" />
        </Related>
    </Fixtures>
    

    YAML Fixtures

    A valid YAML fixtures file is:

    Awesome\Object:
         o1:
             Title: My title
             MyFoo: bar
    
    Awesome\Related:
         r1:
             ObjectId: o1
             Description: Hello world !
    
    Awesome\Tag:
        t1:
            name: Foo
        t2:
            name: Baz
    
    Awesome\Post:
        p1:
            title: A Post with tags (N-N relation)
            tags: [ t1, t2 ]
    

    Using Faker in YAML Fixtures

    If you use Faker with its Symfony2 integration, then the PropelBundle offers a facility to use the Faker generator in your YAML files:

    Acme\DemoBundle\Model\Book:
        Book1:
            name:        "Awesome Feature"
            description: <?php $faker('text', 500); ?>
    

    The aim of this feature is to be able to mix both real and fake data in the same file. Fake data is interesting to quickly add data to your application, but most of the time you need to rely on real data. Integrating Faker in with your YAML files allows you to write strong fixtures efficiently.

    Dumping data

    You can dump data from your database into YAML fixtures file by using this command:

    > php app/console propel:fixtures:dump [--connection[="..."]]
    

    Dumped files will be written in the fixtures directory: app/propel/fixtures/ with the following name: fixtures_99999.yml where 99999 is a timestamp.

    Once done, you will be able to load these files by using the propel:fixtures:load command.

    ACL Implementation

    The PropelBundle provides a model-based implementation of the Security components' interfaces. To make us of this AuditableAclProvider you only need to change your security configuration.

    security:
        acl:
            provider: propel.security.acl.provider
    

    This will switch the provider to be the AuditableAclProvider of the PropelBundle.

    The auditing of this provider is set to a sensible default. It will audit all ACL failures but no success by default. If you also want to audit successful authorizations, you need to update the auditing of the given ACL accordingly.

    After adding the provider, you only need to run the propel:acl:init command in order to get the model generated. If you already got an ACL database, the schema of the PropelBundle is compatible with the default schema of Symfony2.

    Separate database connection for ACL

    In case you want to use a different database for your ACL than your business model, you only need to configure this service.

    services:
        propel.security.acl.connection:
            class: PropelPDO
            factory_class: Propel
            factory_method: getConnection
            arguments:
                - "acl"
    

    The PropelBundle looks for this service, and if given uses the provided connection for all ACL related operations. The given argument (acl in the example) is the name of the connection to use, as defined in your runtime configuration.

    The PropelParamConverter

    You can use the PropelParamConverter with the SensioFrameworkExtraBundle. You just need to put the right Annotation on top of your controller:

    <?php
    
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
    [...]
    
    /**
     * @ParamConverter("post", class="BlogBundle\Model\Post")
     */
    public function myAction(Post $post)
    {
    }
    

    Your request needs to have an id parameter or any field as parameter (slug, title, ...).

    The Annotation is optional if your parameter is typed you could only have this:

    <?php
    
    public function myAction(Post $post)
    {
    }
    

    New with last version of SensioFrameworkExtraBundle, you can omit the class parameter if your controller parameter is typed, this is useful when you need to set extra options.

    <?php
    use BlogBundle\Model\Post;
    
    /**
     * @ParamConverter("post")
     */
    public function myAction(Post $post)
    {
    }
    

    Exclude some parameters

    You can exclude some attributes from being used by the converter:

    If you have a route like /my-route/{slug}/{name}/edit/{id} you can exclude name and slug by setting the option "exclude":

    <?php
    
    /**
     * @ParamConverter("post", class="BlogBundle\Model\Post", options={"exclude"={"name", "slug"}})
     */
    public function myAction(Post $post)
    {
    }
    

    Custom mapping

    You can map route parameters directly to model column to be use for filtering.

    If you have a route like /my-route/{postUniqueName}/{AuthorId} Mapping option overwrite any other automatic mapping.

    <?php
    
    /**
     * @ParamConverter("post", class="BlogBundle\Model\Post", options={"mapping"={"postUniqueName":"name"}})
     * @ParamConverter("author", class="BlogBundle\Model\Author", options={"mapping"={"AuthorId":"id"}})
     */
    public function myAction(Post $post, $author)
    {
    }
    

    Hydrate related object

    You could hydrate related object with the "with" option:

    <?php
    
    /**
     * @ParamConverter("post", class="BlogBundle\Model\Post", options={"with"={"Comments"}})
     */
    public function myAction(Post $post)
    {
    }
    

    You can set multiple with "with"={"Comments", "Author", "RelatedPosts"}.

    The default join is an "inner join" but you can configure it to be a left join, right join or inner join :

    <?php
    
    /**
     * @ParamConverter("post", class="BlogBundle\Model\Post", options={"with"={ {"Comments", "left join" } }})
     */
    public function myAction(Post $post)
    {
    }
    

    Accepted parmeters for join :

    • left, LEFT, left join, LEFT JOIN, left_join, LEFT_JOIN
    • right, RIGHT, right join, RIGHT JOIN, right_join, RIGHT_JOIN
    • inner, INNER, inner join, INNER JOIN, inner_join, INNER_JOIN

    What's Next?

    Now you are ready to use Propel with Symfony2. If you are interested, you can also checkout these cookbooks:

    Found a typo ? Something is wrong in this documentation ? Just fork and edit it !