发布于 2015-08-27 16:34:56 | 595 次阅读 | 评论: 0 | 来源: 网络整理
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.
In this section, you’ll configure your database, create a Product
object, persist it to the database and fetch it back out.
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:
# 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
):
propel: dbal: driver: "%database_driver%" user: "%database_user%" password: "%database_password%" dsn: "%database_driver%:host=%database_host%;dbname=%database_name%;charset=%database_charset%"
注解
Defining the configuration via parameters.yml
is a 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:
$ php app/console propel:database:create
注解
In this example, you have one configured connection, named default
. If you want to configure more than one connection, read the PropelBundle configuration section.
In the Propel world, ActiveRecord classes are known as models because classes generated by Propel contain some business logic.
注解
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:
<!-- src/AppBundle/Resources/config/schema.xml --> <?xml version="1.0" encoding="UTF-8" ?> <database name="default" namespace="AppBundleModel" defaultIdMethod="native"> <table name="product"> <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" /> <column name="name" type="varchar" primaryString="true" size="100" /> <column name="price" type="decimal" /> <column name="description" type="longvarchar" /> </table> </database>
After creating your schema.xml
, generate your model from it by running:
$ php app/console propel:model:build
This generates each model class to quickly develop your application in the Model/
directory of the AppBundle bundle.
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:
$ 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.
小技巧
You can run the last three commands combined by using the following command:
$ php app/console propel:build --insert-sql
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 AppBundleModelProduct; use SymfonyComponentHttpFoundationResponse; 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.
注解
If you’re following along with this example, you’ll need to create a route that points to this action to see it in action.
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 AppBundleModelProductQuery; 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 } }
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 AppBundleModelProductQuery; 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->redirectToRoute('homepage'); } }
Updating an object involves just three steps:
Deleting an object is very similar to updating, but requires a call to the delete()
method on the object:
$product->delete();
Propel provides generated Query
classes to run both basic and complex queries without any work:
use AppBundleModelProductQuery; // ... 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 AppBundleModelProductQuery; // ... $products = ProductQuery::create() ->filterByPrice(array('min' => 19.99)) ->orderByPrice() ->find();
In one line, you get your products in a powerful oriented object 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 AppBundleModelProductQuery; // ... ProductQuery::create() ->orderByName() ->find();
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
:
<?xml version="1.0" encoding="UTF-8" ?> <database name="default" namespace="AppBundleModel" defaultIdMethod="native"> <table name="product"> <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" /> <column name="name" type="varchar" primaryString="true" size="100" /> <column name="price" type="decimal" /> <column name="description" type="longvarchar" /> <column name="category_id" type="integer" /> <foreign-key foreignTable="category"> <reference local="category_id" foreign="id" /> </foreign-key> </table> <table name="category"> <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" /> <column name="name" type="varchar" primaryString="true" size="100" /> </table> </database>
Create the classes:
$ 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.
$ php app/console propel:migration:generate-diff $ php app/console propel:migration:migrate
Your database has been updated, you can continue writing your application.
You will find more information on relations by reading the dedicated chapter on Relationships.
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()
postInsert()
preUpdate()
postUpdate()
preSave()
postSave()
preDelete()
postDelete()
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.
You should read the dedicated section for Propel commands in Symfony2.