2009-06-18

内嵌表单详解(How to Embed Forms in Symfony 1.2 Admin Generator 中文版)

类归于: symfony — 标签:, , maker @ 23:31

译者:Maker.Wang[at]gmail.com
原文:http://sandbox-ws.com/how-to-embed-forms-in-symfony-12-admin-generator

译者注: Embed form是一个被忽略了的却很实用的技术,可能是由于embed form是新功能的缘故,相关的文档非常少,特别是中文文档,很早以前就听说了embed form,但一直没有机会实际应用, 昨天项目的新需求让我又想起了embed form, 所以又翻出了以前看过的文档, 仔细复习一遍, 同时也翻译出来分享个各位战友们。这是小弟的第一篇译文,难免有错误和遗漏,希望大家多包涵,多提宝贵意见。 – maker 2009.6.19

第一部分. 一对一关联内嵌表单

Symfony 1.2 添加了很多令人兴奋的新特性,并已经成为了一个很好很强大的PHP开发框架。 其中的一个特性是将一个Form嵌入另一个Form中。那么这意味着什么呢?

第一个模型如下图所示:

company_contact 上图是一个公司和联系方式的一对一关系模型。

这是我们的 schema.yml:

  1. propel:
  2. _attributes:
  3. package: lib.model
  4. defaultIdMethod: native
  5. company:
  6. _attributes: { phpName: Company }
  7. id: { type: INTEGER, size: ‘11′, primaryKey: true, autoIncrement: true, required: true }
  8. name: { type: VARCHAR, size: ‘255′, required: true }
  9. contact_id: { type: INTEGER, size: ‘11′, required: false, foreignTable: contact, foreignReference: id, onDelete: SET NULL, onUpdate: RESTRICT }
  10. _indexes: { company_FI_1: [contact_id] }
  11. contact:
  12. _attributes: { phpName: Contact }
  13. id: { type: INTEGER, size: ‘11′, primaryKey: true, autoIncrement: true, required: true }
  14. first_name: { type: VARCHAR, size: ‘255′, required: true }
  15. last_name: { type: VARCHAR, size: ‘255′, required: true }
  16. company_id: { type: INTEGER, size: ‘11′, required: false, foreignTable: company, foreignReference: id, onDelete: SET NULL, onUpdate: RESTRICT }
  17. _indexes: { contact_FI_1: [company_id] }

要注意的是这里的两个外键并不是必须存在(required)的,但是必须有了这两个外键我们才能将Contact Form嵌入到Company Form。

现在我们已经有了Schema, 让我们生成sql语句,模型(models), 表单(forms), 过滤器(filters), 并且创建数据表。

  1. $ php symfony propel:build-sql
  2. $ php symfony propel:build-model
  3. $ php symfony propel:build-forms
  4. $ php symfony propel:build-filters
  5. $ php symfony propel:insert-sql –env=dev

译者注:上面这组命令我们也可以简单的使用一个命令来完成

  1. $ php symfony propel:build-all

现在我们使用symfony中的admin generator来生成一个模块, 来看看如何生成和操作他们之间的关联。

  1. $ php symfony propel:generate-admin backend Company

我们生成了如下图所示的模块:

company_1现在如果我们点击“New”链接我们将看到界面如下:

company_2

注意外键”contact_id”被admin generator处理成一个下拉列表,当然这不是我们想要的结果,我们需要在新建Company的同时创建Contact. 感谢symfony 1.2 支持表单内嵌, 这会使我们的工作变得非常简单。

首先打开 “CompanyForm.class.php” 并且输入代码如下:

  1. public function configure() {
  2. // get Contact model object
  3. $contact = $this->getObject()->getContact();
  4. // contact object is null
  5. if (is_null($contact)) {
  6. // create a new Contact object
  7. $contact = new Contact();
  8. // set the copmany of the newly created object to the current company
  9. $contact->setCompany($this->getObject());
  10. // set the contact of the current company
  11. $this->getObject()->setContact($contact);
  12. }
  13. // create a new contact form
  14. $contact_form = new ContactForm($contact);
  15. // embed the contact form in the current company form
  16. $this->embedForm(‘contact’, $contact_form);
  17. // remove the contact_id from the form
  18. unset($this['contact_id']);
  19. }

接下来,打开ContactForm.class.php 并输入代码如下:

  1. public function configure() {
  2. unset($this['company_id']);
  3. }

现在修改模型类Company.php,在删除Contact的同时删除关联的Company

  1. public function delete(PropelPDO $con = null) {
  2. $this->getContact()->delete($con);
  3. parent::delete($con);
  4. }

最后一点也是很重要的,编辑generator.yml如下(这一步不是必须得) :

  1. generator:
  2. class: sfPropelGenerator
  3. param:
  4. model_class:           Company
  5. theme:                 admin
  6. non_verbose_templates: true
  7. with_show:             false
  8. singular:              ~
  9. plural:                ~
  10. route_prefix:          company
  11. with_propel_route:     1
  12. config:
  13. actions: ~
  14. fields:
  15. contact_id: { label: Company Contact }
  16. list:
  17. display: [=name, contact]
  18. filter:  ~
  19. form:    ~
  20. edit:    ~
  21. new:     ~

现在进入新建”Company”页面你会看到如图所示:

company_3尝试添加,编辑和删除一些记录以便确认一切是正确的。

company_4

company_5

第二部分. 一对多关联的内嵌表单

在文章的第一部分我们讲解了如何实一对一关联的表单内嵌。但是我们经常需要在编辑父对象的时候编辑或者添加子对象, 这并不是我们要使用admin generator的原因,总之在这部分文章里我们将要讲解一对多关系的表单内嵌。

在开始之前,我们看到的是最终要实现的CategoryForm。

embed_2

本文需要的文件

Ok, 我们开始吧,考虑下面这个schema:

  1. category:
  2. _attributes: { phpName: Category }
  3. id: { type: INTEGER, size: ‘11′, primaryKey: true, autoIncrement: true, required: true }
  4. name: { type: VARCHAR, size: ‘255′, required: true }
  5. created_at: { type: TIMESTAMP, required: false }
  6. updated_at: { type: TIMESTAMP, required: false }
  7. subcategory:
  8. _attributes: { phpName: Subcategory }
  9. id: { type: INTEGER, size: ‘11′, primaryKey: true, autoIncrement: true, required: true }
  10. name: { type: VARCHAR, size: ‘255′, required: true }
  11. category_id: { type: INTEGER, size: ‘11′, required: true, foreignTable: category, foreignReference: id, onDelete: CASCADE, onUpdate: RESTRICT }
  12. created_at: { type: TIMESTAMP, required: false }
  13. updated_at: { type: TIMESTAMP, required: false }

为了实现想要的效果,我们需要需要采取以下步骤

  1. 修改CategoryForm去包含当前分类所有的子分类的内嵌表单。
  2. 修改Widget中的子分类的name
  3. 修改CategoryForm添加一个空白的创建新子分类的表单。
  4. 重写sfForm类的bind方法,如果name域是空白则跳过保存(saving)和验证(validating)新子分类的表单。
  5. 在SubcategoryForm去掉全部的域(fields)

为当前分类插入全部子分类的内嵌表单[第1,2,3步]

  1. // lib/forms/CategoryForm.class.php
  2. public function configure() {
  3. // remove timestamps
  4. unset($this['created_at'], $this['updated_at']);
  5. // embed forms only when editing
  6. if (!$this->isNew()) {
  7. // embed all subcategory forms
  8. foreach ($this->getObject()->getSubcategorys() as $subcategory) {
  9. // create a new subcategory form for the current subcategory model object
  10. $subcategory_form = new SubcategoryForm($subcategory);
  11. // embed the subcategory form in the main category form
  12. $this->embedForm(’subcategory’.$subcategory->getId(), $subcategory_form);
  13. // set a custom label for the embedded form
  14. $this->widgetSchema['subcategory'.$subcategory->getId()]->setLabel(‘Subcategory: ’.$subcategory->getName());
  15. // change the name widget to sfWidgetFormInputDelete
  16. $this->widgetSchema['subcategory'.$subcategory->getId()]['name'] = new sfWidgetFormInputDelete(array(
  17. ‘url’ => ‘category/deleteSubcategory’, // required
  18. ‘model_id’ => $subcategory->getId(), // required
  19. ‘confirm’ => ‘Sure???’, // optional
  20. ));
  21. }
  22. // create a new subcategory form for a new subcategory model object
  23. $subcategory_form = new SubcategoryForm();
  24. // embed the subcategory form in the main category form
  25. $this->embedForm(’subcategory’, $subcategory_form);
  26. // set a custom label for the embedded form
  27. $this->widgetSchema['subcategory']->setLabel(‘New Subcategory’);
  28. }
  29. }

重写bind方法

  1. public function bind(array $taintedValues = null, array $taintedFiles = null) {
  2. // remove the embedded new form if the name field was not provided
  3. if (is_null($taintedValues['subcategory']['name']) || strlen($taintedValues['subcategory']['name']) === 0 ) {
  4. unset($this->embeddedForms['subcategory'], $taintedValues['subcategory']);
  5. // pass the new form validations
  6. $this->validatorSchema['subcategory'] = new sfValidatorPass();
  7. else {
  8. // set the category of the new subcategory form object
  9. $this->embeddedForms['subcategory']->getObject()->setCategory($this->getObject());
  10. }
  11. // call parent bind method
  12. parent::bind($taintedValues, $taintedFiles);
  13. }

在SubcategoryForm中的域

  1. public function configure(){
  2. unset($this['created_at'], $this['updated_at'], $this['category_id']);
  3. }

现在我们在分类模块下创建deleteSubcategory动作

  1. // apps/backend/modules/category/actions/actions.class.php
  2. public function executeDeleteSubcategory(sfWebRequest $request) {
  3. $sub_category = SubcategoryPeer::retrieveByPk($request->getParameter(‘id’));
  4. $sub_category->delete();
  5. $this->redirect(‘@category_edit?id=’.$sub_category->getCategory()->getId());
  6. }

就是这样了,我希望你会喜欢这篇文章。

第三部分. 内嵌表单的本地化

在本系列文章的上一部分我们介绍了在父对象中编辑多个子对象的方法。 在本部分钟我们将给子对象添加本地化行为, 为了更清楚的知道我们要做的事先看一下最终的效果。

embed_i18n

在开始之前,如果你没准备好,我强烈建议你去阅读一下第1,2部分。 OK, 我们开始吧,我们将要修改以下文件:

  • CategoryForm.class.php 分类模型的表单
  • SubcategoryI18nForm.class.php 子分类模型的本地化模型的表单
  • Subcategory.class.php 子分类模型

本文的附件

Schema.yml

  1. category:
  2. _attributes: { phpName: Category }
  3. id: { type: INTEGER, size: ‘11′, primaryKey: true, autoIncrement: true, required: true }
  4. name: { type: VARCHAR, size: ‘255′, required: true }
  5. created_at: { type: TIMESTAMP, required: false }
  6. updated_at: { type: TIMESTAMP, required: false }
  7. subcategory:
  8. _attributes: { phpName: Subcategory, isI18N: true, i18nTable: subcategory_i18n }
  9. id:          { type: integer, required: true, primaryKey: true, autoincrement: true }
  10. category_id: { type: INTEGER, size: ‘11′, required: true, foreignTable: category, foreignReference: id, onDelete: CASCADE}
  11. created_at: { type: TIMESTAMP, required: false }
  12. updated_at: { type: TIMESTAMP, required: false }
  13. subcategory_i18n:
  14. _attributes: { phpName: SubcategoryI18n }
  15. id:          { type: integer, required: true, primaryKey: true, foreignTable: subcategory, foreignReference: id, onDelete: CASCADE }
  16. culture:     { isCulture: true, type: varchar, size: 7, required: true, primaryKey: true }
  17. name:        { type: varchar, size: 50 }

SubcategoryI18nForm.class.php

  1. public function configure() {
  2. unset($this['culture'], $this['id']);
  3. }

Subcategory.class.php

  1. public function getI18nObject($culture = ‘en’) {
  2. $this->setCulture($culture);
  3. $i18ns = $this->getSubcategoryI18ns();
  4. if (isset($i18ns[0])) {
  5. return $i18ns[0];
  6. }
  7. return null;
  8. }

CategoryForm.class.php

  1. public function configure() {
  2. // remove timestamps
  3. unset($this['created_at'], $this['updated_at']);
  4. // embed forms only when editing
  5. if (!$this->isNew()) {
  6. $user_culture = sfContext::getInstance()->getUser()->getCulture();
  7. // embed all subcategory forms
  8. foreach ($this->getObject()->getSubcategorys() as $subcategory) {
  9. // get the subcategory_i18n model object relative to the current user culture
  10. $subcategory_i18n_object = $subcategory->getI18nObject($user_culture);
  11. // create a new subcategory_i18n form for the current subcategory model object
  12. $subcategory_i18n_form = new SubcategoryI18nForm($subcategory->getI18nObject(‘en’));
  13. // get widget schema of subcategory_i18n form
  14. $subcategory_i18n_form_widget_schema = $subcategory_i18n_form->getWidgetSchema();
  15. // set the input delete widget
  16. $subcategory_i18n_form_widget_schema['name'] = new sfWidgetFormInputDelete(array(
  17. ‘url’ => ‘category/deleteSubcategory’, // required
  18. ‘model_id’ => $subcategory->getId(), // required
  19. ‘confirm’ => ‘Sure???’, // optional
  20. ));
  21. // create a new subcategory form for the current subcategory model object
  22. $subcategory_form = new SubcategoryForm($subcategory);
  23. // embed the i18n form
  24. $subcategory_form->embedForm(’subcategory_i18n’.$subcategory_i18n_object->getId(), $subcategory_i18n_form);
  25. // subcategory form widget schema
  26. $subcategory_form_widget_schema = $subcategory_form->getWidgetSchema();
  27. // disable label
  28. $subcategory_form_widget_schema['subcategory_i18n'.$subcategory_i18n_object->getId()]->setLabel(false);
  29. // embed the subcategory form in the main category form
  30. $this->embedForm(’subcategory’.$subcategory->getId(), $subcategory_form);
  31. // set a custom label for the embedded form
  32. $this->widgetSchema['subcategory'.$subcategory->getId()]->setLabel(‘Subcategory: ’.$subcategory->getName());
  33. }
  34. // create a new subcategory form for a new subcategory model object
  35. $subcategory_form = new SubcategoryForm();
  36. // create a new subcategory form for a new subcategory_i18n model object
  37. $subcategory_i18n_form = new SubcategoryI18nForm();
  38. // embed the subcategory_i18n form in the parent subcategory form
  39. $subcategory_form->embedForm(’subcategory_i18n’, $subcategory_i18n_form);
  40. // subcategory form widget schema
  41. $subcategory_form_widget_schema = $subcategory_form->getWidgetSchema();
  42. // disable label
  43. $subcategory_form_widget_schema['subcategory_i18n']->setLabel(false);
  44. // embed the subcategory form in the main category form
  45. $this->embedForm(’subcategory’, $subcategory_form);
  46. // set a custom label for the embedded form
  47. $this->widgetSchema['subcategory']->setLabel(‘New Subcategory’);
  48. }
  49. }
  50. public function bind(array $taintedValues = null, array $taintedFiles = null) {
  51. if(!$this->isNew()) {
  52. $user_culture = sfContext::getInstance()->getUser()->getCulture();
  53. // remove the embedded new form if the name field was not provided
  54. if (is_null($taintedValues['subcategory']['subcategory_i18n']['name']) || strlen($taintedValues['subcategory']['subcategory_i18n']['name']) === 0 ) {
  55. unset($this->embeddedForms['subcategory'], $taintedValues['subcategory']);
  56. // pass the new form validations
  57. $this->validatorSchema['subcategory'] = new sfValidatorPass();
  58. else {
  59. // set the category of the new subcategory form object
  60. $this->embeddedForms['subcategory']->getObject()->setCategory($this->getObject());
  61. // get subcategory embedded forms
  62. $subcategory_embedded_forms = $this->embeddedForms['subcategory']->getEmbeddedForms();
  63. // set subcategory parent of the subcategory_i18n model object
  64. $subcategory_embedded_forms['subcategory_i18n']->getObject()->setSubcategory($this->embeddedForms['subcategory']->getObject());
  65. // set the culture of the subcategory_i18n model object
  66. $subcategory_embedded_forms['subcategory_i18n']->getObject()->setCulture($user_culture);
  67. }
  68. }
  69. // call parent bind method
  70. parent::bind($taintedValues, $taintedFiles);
  71. }

正如你所看到的,这是功能需要你复制和粘贴大量的代码,这就是为什么我在使用扩展的Admin Generator,  为了当你生成管理模块的时候有此功能可用。

2009-02-27

symfony数据表关联详解(for propel)

类归于: symfony — 标签:, , , , maker @ 16:52

上周又接到一个研究性质的任务,最近比较忙碌,所以这两天才开始分析代码,由于数据表相关的内容会比较多,所以本文将分成以下几部分。

  1. 商店的数据关系
  2. 一对多(hasMany)关系的处理
  3. 从属(belongsTo)关系的处理
  4. 多对多(manyToMany)关系的处理
  5. 一对一(oneToOne)关系的处理
  6. 总结

本文使用的测试环境如下:

  • php 5.2.6
  • apache 2.2.8
  • mysql 5.0.67
  • symfony 1.2.4-DEV

一. 商店的数据关系

我们用一个例子来对symfony中的表关联进行分析,我们假设商店有如下的数据关系。

图1.1

2009-02-26-104551_294x299_scrot

这里涉及到4类对象实体,分别是City(城市),Shop(商店),Shopinfo(商店信息), Tag(标签)。其中又涉及到了三种数据关系,一个城市可以有多家商店(一对多),一个商店拥有一个商店信息(一对一),一个商店可以拥有很多特点(多对多),这里我们用Tag来表示特点(特性)。

首先我们设计数据库结构config/schema.yml

图1.2

2009-02-26-100743_401x689_scrot

然后我们自动生成相关的库文件

$ ./symfony propel:build-all

首先我们来分析City和Shop之间的一对多/从属关系.

二.一对多(hasMany)关系的处理

我们先来看propel自动生成的BaseCity对象

图1.3

2009-02-25-103053_396x688_scrot

其他和关联没有关系的表这里不做过多描述, 我们重点介绍下面几个方法.

clearShops();

清空当前已经获得的关联商店, 实际上这个清空操作并没有真正意义上的进行清空或者删除,只是清空了当前取得的关联对象集合.

示例2.1(通过测试):

$mc = new Shop;
$mc->setName('MC in Beijing');
$city->addShop($mc);
$city->initShops();
$wc = new Shop;
$wc->setName('WC in Beijing');
$city->addShop($wc);
$city->save();

initShops();

清空当前已经获得的关联商店, 实际上这个清空操作并没有真正意义上的进行清空或者删除,只是清空了当前取得的关联对象集合. 该方法与clearShops()类似, 但该方法将Shop集合初始化为一个空的数组而clearShops()将Shop集合设置为null.

示例2.2(通过测试):

$mc = new Shop;
$mc->setName('MC in Beijing');
$city->addShop($mc);
$city->initShops();
$wc = new Shop;
$wc->setName('WC in Beijing');
$city->addShop($wc);
$city->save();

getShops($criteria, $con);

根据条件$criteria返回city下相关联的shop对象集合.

示例2.3(通过测试):

//我们用下面代码获得某城市下全部的KFC(肯德基)店铺
$c = new Criteria;
$c->add(ShopPeer::NAME, '%KFC%', Criteris::LIKE);
foreach($city->getShops($c) as $shop){
echo $shop->getId();
}

countShops($criteria, $con);

countShops()方法用来返回满组织定条件的关联Shop个数

示例2.4(通过测试):

//我们用下面代码获得某城市下全部的KFC(肯德基)的个数
$c = new Criteria;
$c->add(ShopPeer::NAME, '%KFC%', Criteris::LIKE);
echo $city->getName() . ' has ' . $city->countShops($c) . ' KFC shops.';

addShop($shop);

给city添加一个关联的shop, 参数$shop是一个shop对象

示例2.5(通过测试):

//我们用下面代码在某城市新加一个KFC店铺
$shop = new Shop;
$shop->setName('KFC 人民大街店');
$city->addShop($shop);
$city->save()

clearAllReferences($deep);

清空当前已经获得的关联对象, 实际上这个清空操作并没有真正意义上的进行清空或者删除,只是清空了当前已经取得的关联对象集合. 由于city只和shop进行了关联, 所以对于city对象来说其实该方法的效果和clearShops()是相同的.

示例2.6:

$mc = new Shop;
$mc->setName('MC in Beijing');
$city->addShop($mc);
$city->clearAllReferences();
$wc = new Shop;
$wc->setName('WC in Beijing');
$city->addShop($wc);
$city->save();

三.从属(belongsTo)关系的处理

下图为BaseShop对象的结构

图3.1 BaseShop

2009-02-26-123737_401x843_scrot

前面很多方法和BaseCity都是一样的, 而与BaseCity不同并与city相关的只有以下方法.

setCity(City $city)

指定所属city, 参数$city是city对象

示例3.1(通过测试):

//添加Changchun和KFC并同时创建关联
$city = new City;
$city->setName('Changchun');
$shop->new Shop;
$shop->setName('KFC in Changchun');
$shop->setCity($city);
$shop->save();

getCity($con)

返回所属城市,返回值为city对象.

示例3.2(通过测试):

// 输出店铺所在城市名
echo $shop->getCity()->getName();

四. 多对多(manyToMany)关系的处理

我们继续看例子, Shop对象和Tag对象通过Shop_Tag对象进行多对多的关联, 下面我们来看一下这三个对象都生成了哪些方法.

图4.1

2009-02-25-172720_395x647_scrot

图4.2

2009-02-25-172752_403x704_scrot

在symfony中多对多关系相对要复杂一些, 整个过程涉及到3种对象, 其中涉及到的方法也要多一些.

其中Shop对象的相关方法有

clearShopTags();

参考示例2.1

initShopTags();

参考示例2.2

getShopTags($criteria)

返回所有满足条件的相关联的ShopTag对象

示例4.1(通过测试):

foreach($shop->getShopTags() as $shopTag){
echo $shopTag->getTag()->getName();
}

countShopTags($criteria);

参考示例2.4

addShopTag($shoptag);

添加一个ShopTag关联, 参数$shoptag是ShopTag对象

我并没有想明白这个方法是用来作什么的, 按照正常的逻辑创建一个多对多关联应该是示例化一个新的ShopTag而不应该在Shop中addShopTag().

示例4.2(通过测试):

$shopTag = new ShopTag;
$shopTag->setTagId(2);
$shop->addShopTag($shopTag);
$shop->save();

getShopTagsJoinTag($criteria);

返回所有符合查询条件的ShopTag关联对象并同时取出关联的Tag对象.

在使用getShopTagsJoinTags()的时候你会发现用法其实和getShopTags()是一样的, 但使用getShopTagJoinTags()的之后再使用shopTag的getTag()方法实际上就不进行数据库查询了. 下面的例子要比getShopTags()的例子少很多次查询.

示例4.3(通过测试):

foreach($shop->getShopTagsJoinTag() as $shopTag){
echo $shopTag->getTag()->getName();
}

clearAllReferences($deep);

参考示例2.6

Tag对象的相关方法有

clearShopTags();

参考示例2.1

initShopTags();

参考示例2.2

getShopTags($criteria)

参考示例4.1

countShopTags($criteria);

参考示例2.4

addShopTag($shoptag);

参考示例4.2

getShopTagsJoinShop($criteria);

参考示例4.3

clearAllReferences($deep);

参考示例2.6

ShopTag对象的相关方法有

getShop();

返回关联的Shop对象

示例4.4(通过测试):

// 输出与tag关联的全部shop的name
foreach($tag->getShopTags() as $shopTag){
echo $shopTag->getShop()->getName();
}

setShop($shop);

设置关联Shop, $shop参数是Shop对象

getTag();

返回关联的Tag对象

示例4.5(通过测试):

// 输出与shop关联的全部tag的name
foreach($shop->getShopTags() as $shopTag){
echo $shopTag->getTag()->getName();
}

setTag($tag);

设置关联Tag, $tag参数是Tag对象

clearAllReferences($deep);

参考示例2.6

五.一对一(oneToOne)关系的处理

首先, 我们来看一下propel自动生成的Shopinfo对象, Shop对象在图3.1中已经有介绍.

图5.1 BaseShopinfo

2009-02-26-123139_398x617_scrot

经过几天的探索才成功的找到了进行一对一关联的方法.我们必须要将shopinfo中的外键设定为shopinfo的主键才可以被propel处理.

下面这几种都是错误的关联设计.

shop:
..id:
..shopinfo_id:
..name:
shopinfo:
..id:
..add:
..tel:

shop:
..id:
..name:
shopinfo:
..id:
..shop_id:
..add:
..tel:

shop:
..id:
..shopinfo_id:
..name:
shopinfo:
..id:
..shop_id:
..add:
..tel:

正确的一对一关联设计应该是

shop:
..id:
..name:
shopinfo:
..shop_id:
..add:
..tel:

根据图3.1图5.1, Shop对象拥有方法setShopinfo()和getShopinfo()方法来操作唯一的关联店铺信息, 而Shopinfo对象也拥有setShop()和getShop()方法来操作唯一的关联店铺.

示例5.1:

//给商店添加店铺信息
$shop->setShopinfo(new Shopinfo);
$shop->getShopinfo()->setAddress('a Place');
$shop->getShopinfo()->setTel('010-12345678');
$shop->save();

示例5.2:

// 修改店铺的店铺信息
$shop->getShopinfo()->setTel('010-87654321');
$shop->save();

六.总结

到这里为止我们就把symfony中propel对数据关联的操作介绍的差不多了, 可能还有一些方面没有详细的介绍也只能考各位自己钻研了. 介于本人能力有限, 本文难免会有错误和遗漏, 希望大家可以指出, 感慨一下, 这篇文章从策划到结束一共用了4天.

附件是本文进行测试的项目文件, 内无symfony库, 请将symfony/lib目录cp到lib目录下并重命名为symfony.

附件: references.tgz

(本文完)

WordPress 所驱动