diff --git a/book/propel.rst b/book/propel.rst index 53b953dd75d..410656289cf 100644 --- a/book/propel.rst +++ b/book/propel.rst @@ -4,529 +4,16 @@ Databases and Propel ==================== -One of the most common and challenging tasks for any application -involves persisting and reading information to and from a database. Symfony -does not come integrated with any ORMs but the Propel integration is easy. -To install Propel, read `Working With Symfony2`_ on the Propel documentation. - -A Simple Example: A Product ---------------------------- - -In this section, you'll configure your database, create a ``Product`` object, -persist it to the database and fetch it back out. - -Configuring the Database -~~~~~~~~~~~~~~~~~~~~~~~~ - -Before you can start, you'll need to configure your database connection -information. By convention, this information is usually configured in an -``app/config/parameters.yml`` file: - -.. code-block:: yaml - - # app/config/parameters.yml - parameters: - database_driver: mysql - database_host: localhost - database_name: test_project - database_user: root - database_password: password - database_charset: UTF8 - -These parameters defined in ``parameters.yml`` can now be included in the -configuration file (``config.yml``): - -.. code-block:: yaml - - propel: - dbal: - driver: "%database_driver%" - user: "%database_user%" - password: "%database_password%" - dsn: "%database_driver%:host=%database_host%;dbname=%database_name%;charset=%database_charset%" - -.. note:: - - Defining the configuration via ``parameters.yml`` is a - :ref:`Symfony Framework Best Practice `, - feel free to do it differently if that suits your application better. - -Now that Propel knows about your database, it can create the database for -you: - -.. code-block:: bash - - $ php app/console propel:database:create - -.. note:: - - In this example, you have one configured connection, named ``default``. If - you want to configure more than one connection, read the - `PropelBundle configuration section`_. - -Creating a Model Class -~~~~~~~~~~~~~~~~~~~~~~ - -In the Propel world, ActiveRecord classes are known as **models** because classes -generated by Propel contain some business logic. - -.. note:: - - For people who use Symfony with Doctrine2, **models** are equivalent to - **entities**. - -Suppose you're building an application where products need to be displayed. -First, create a ``schema.xml`` file inside the ``Resources/config`` directory -of your AppBundle: - -.. code-block:: xml - - - - - - - - - - - - -
-
- -Building the Model -~~~~~~~~~~~~~~~~~~ - -After creating your ``schema.xml``, generate your model from it by running: - -.. code-block:: bash - - $ php app/console propel:model:build - -This generates each model class to quickly develop your application in the -``Model/`` directory of the AppBundle bundle. - -Creating the Database Tables/Schema -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now you have a usable ``Product`` class and all you need to persist it. Of -course, you don't yet have the corresponding ``product`` table in your -database. Fortunately, Propel can automatically create all the database tables -needed for every known model in your application. To do this, run: - -.. code-block:: bash - - $ php app/console propel:sql:build - $ php app/console propel:sql:insert --force - -Your database now has a fully-functional ``product`` table with columns that -match the schema you've specified. - -.. tip:: - - You can run the last three commands combined by using the following - command: - - .. code-block:: bash - - $ php app/console propel:build --insert-sql - -Persisting Objects to the Database -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now that you have a ``Product`` object and corresponding ``product`` table, -you're ready to persist data to the database. From inside a controller, this -is pretty easy. Add the following method to the ``ProductController`` of the -bundle:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\Product; - use Symfony\Component\HttpFoundation\Response; - - class ProductController extends Controller - { - public function createAction() - { - $product = new Product(); - $product->setName('A Foo Bar'); - $product->setPrice(19.99); - $product->setDescription('Lorem ipsum dolor'); - - $product->save(); - - return new Response('Created product id '.$product->getId()); - } - } - -In this piece of code, you instantiate and work with the ``$product`` object. -When you call the ``save()`` method on it, you persist it to the database. No -need to use other services, the object knows how to persist itself. - -.. note:: - - If you're following along with this example, you'll need to create a - :doc:`route ` that points to this action to see it in action. - -Fetching Objects from the Database -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Fetching an object back from the database is even easier. For example, suppose -you've configured a route to display a specific ``Product`` based on its ``id`` -value:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\ProductQuery; - - class ProductController extends Controller - { - // ... - - public function showAction($id) - { - $product = ProductQuery::create()->findPk($id); - - if (!$product) { - throw $this->createNotFoundException( - 'No product found for id '.$id - ); - } - - // ... do something, like pass the $product object into a template - } - } - -Updating an Object -~~~~~~~~~~~~~~~~~~ - -Once you've fetched an object from Propel, updating it is easy. Suppose you -have a route that maps a product id to an update action in a controller:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\ProductQuery; - - class ProductController extends Controller - { - // ... - - public function updateAction($id) - { - $product = ProductQuery::create()->findPk($id); - - if (!$product) { - throw $this->createNotFoundException( - 'No product found for id '.$id - ); - } - - $product->setName('New product name!'); - $product->save(); - - return $this->redirect($this->generateUrl('homepage')); - } - } - -Updating an object involves just three steps: - -#. fetching the object from Propel (line 12 - 18); -#. modifying the object (line 20); -#. saving it (line 21). - -Deleting an Object -~~~~~~~~~~~~~~~~~~ - -Deleting an object is very similar to updating, but requires a call to the -``delete()`` method on the object:: - - $product->delete(); - -Querying for Objects --------------------- - -Propel provides generated ``Query`` classes to run both basic and complex queries -without any work:: - - use AppBundle\Model\ProductQuery; - // ... - - ProductQuery::create()->findPk($id); - - ProductQuery::create() - ->filterByName('Foo') - ->findOne(); - -Imagine that you want to query for products which cost more than 19.99, ordered -from cheapest to most expensive. From inside a controller, do the following:: - - use AppBundle\Model\ProductQuery; - // ... - - $products = ProductQuery::create() - ->filterByPrice(array('min' => 19.99)) - ->orderByPrice() - ->find(); - -In one line, you get your products in a powerful object-oriented way. No need -to waste your time with SQL or whatever, Symfony offers fully object-oriented -programming and Propel respects the same philosophy by providing an awesome -abstraction layer. - -If you want to reuse some queries, you can add your own methods to the -``ProductQuery`` class:: - - // src/AppBundle/Model/ProductQuery.php - - // ... - class ProductQuery extends BaseProductQuery - { - public function filterByExpensivePrice() - { - return $this->filterByPrice(array( - 'min' => 1000, - )); - } - } - -However, note that Propel generates a lot of methods for you and a simple -``findAllOrderedByName()`` can be written without any effort:: - - use AppBundle\Model\ProductQuery; - // ... - - ProductQuery::create() - ->orderByName() - ->find(); - -Relationships/Associations --------------------------- - -Suppose that the products in your application all belong to exactly one -"category". In this case, you'll need a ``Category`` object and a way to relate -a ``Product`` object to a ``Category`` object. - -Start by adding the ``category`` definition in your ``schema.xml``: - -.. code-block:: xml - - - - - - - - - - - - - - - - - - -
- - - - - -
-
- -Create the classes: - -.. code-block:: bash - - $ php app/console propel:model:build - -Assuming you have products in your database, you don't want to lose them. Thanks to -migrations, Propel will be able to update your database without losing existing -data. - -.. code-block:: bash - - $ php app/console propel:migration:generate-diff - $ php app/console propel:migration:migrate - -Your database has been updated, you can continue writing your application. - -Saving Related Objects -~~~~~~~~~~~~~~~~~~~~~~ - -Now, try the code in action. Imagine you're inside a controller:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\Category; - use AppBundle\Model\Product; - use Symfony\Component\HttpFoundation\Response; - - class ProductController extends Controller - { - public function createProductAction() - { - $category = new Category(); - $category->setName('Main Products'); - - $product = new Product(); - $product->setName('Foo'); - $product->setPrice(19.99); - // relate this product to the category - $product->setCategory($category); - - // save the whole - $product->save(); - - return new Response( - 'Created product id: '.$product->getId().' and category id: '.$category->getId() - ); - } - } - -Now, a single row is added to both the ``category`` and ``product`` tables. The -``product.category_id`` column for the new product is set to whatever the id is -of the new category. Propel manages the persistence of this relationship for -you. - -Fetching Related Objects -~~~~~~~~~~~~~~~~~~~~~~~~ - -When you need to fetch associated objects, your workflow looks just like it did -before: Fetch a ``$product`` object and then access its related ``Category``:: - - // src/AppBundle/Controller/ProductController.php - - // ... - use AppBundle\Model\ProductQuery; - - class ProductController extends Controller - { - public function showAction($id) - { - $product = ProductQuery::create() - ->joinWithCategory() - ->findPk($id); - - $categoryName = $product->getCategory()->getName(); - - // ... - } - } - -Note, in the above example, only one query was made. - -More Information on Associations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You will find more information on relations by reading the dedicated chapter on -`Relationships`_. - -Lifecycle Callbacks -------------------- - -Sometimes, you need to perform an action right before or after an object is -inserted, updated, or deleted. These types of actions are known as "lifecycle" -callbacks or "hooks", as they're callback methods that you need to execute -during different stages of the lifecycle of an object (e.g. the object is -inserted, updated, deleted, etc). - -To add a hook, just add a new method to the object class:: - - // src/AppBundle/Model/Product.php - - // ... - class Product extends BaseProduct - { - public function preInsert(\PropelPDO $con = null) - { - // ... do something before the object is inserted - } - } - -Propel provides the following hooks: - -``preInsert()`` - Code executed before insertion of a new object. -``postInsert()`` - Code executed after insertion of a new object. -``preUpdate()`` - Code executed before update of an existing object. -``postUpdate()`` - Code executed after update of an existing object. -``preSave()`` - Code executed before saving an object (new or existing). -``postSave()`` - Code executed after saving an object (new or existing). -``preDelete()`` - Code executed before deleting an object. -``postDelete()`` - Code executed after deleting an object. - -Behaviors ---------- - -All bundled behaviors in Propel are working with Symfony. To get more -information about how to use Propel behaviors, look at the -`Behaviors reference section`_. - -Commands --------- - -You should read the dedicated section for `Propel commands in Symfony2`_. - -.. _`Working With Symfony2`: http://propelorm.org/Propel/cookbook/symfony2/working-with-symfony2.html#installation -.. _`PropelBundle configuration section`: http://propelorm.org/Propel/cookbook/symfony2/working-with-symfony2.html#configuration -.. _`Relationships`: http://propelorm.org/Propel/documentation/04-relationships.html -.. _`Behaviors reference section`: http://propelorm.org/Propel/documentation/#behaviors-reference -.. _`Propel commands in Symfony2`: http://propelorm.org/Propel/cookbook/symfony2/working-with-symfony2#the-commands +Propel is an open-source Object-Relational Mapping (ORM) for PHP which +implements the `ActiveRecord pattern`_. It allows you to access your database +using a set of objects, providing a simple API for storing and retrieving data. +Propel uses PDO as an abstraction layer and code generation to remove the +burden of runtime introspection. + +A few years ago, Propel was a very popular alternative to Doctrine. However, its +popularity has rapidly declined and that's why the Symfony book no longer includes +the Propel documentation. Read the `official PropelBundle documentation`_ to learn +how to integrate Propel into your Symfony projects. + +.. _`ActiveRecord pattern`: https://en.wikipedia.org/wiki/Active_record_pattern +.. _`official PropelBundle documentation`: https://github.com/propelorm/PropelBundle/blob/1.4/Resources/doc/index.markdown