2009-03-24

Add table prefix in symfony-在symfony中使用表前缀

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

The propel 1.3 is support table prefixes.

In your config/propel.ini add the following:

propel.tablePrefix=prefix_

then run the propel:build-all-load task.

Done!

网上找不到任何关于symfony或者propel支持表前缀的资料,只在propel1.3的修改历史中发现了1.3是支持表前缀的,但文档却没有更新相关内容,无奈之下只能查看源码,最后终于让我找到了。

补遗:还有一种方法我觉得是更加可靠的,那就是手动修改schema.yml中的全部数据库名,手动加前缀,同时强制设定表的phpName保证其不会随着表名一同变化,然后重新./symfony propel:build-all

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

(本文完)

2008-12-15

关于数据查询的使用

类归于: symfony — 标签:, , kthiz2006 @ 18:44
目前常用的数据库查询采用的是Criteria。

常用的sql如下:
WHERE column = value 	->add(column, value);
WHERE column <> value 	->add(column, value, Criteria::NOT_EQUAL);
Other Comparison Operators
> , < 	Criteria::GREATER_THAN, Criteria::LESS_THAN
>=, <= 	Criteria::GREATER_EQUAL, Criteria::LESS_EQUAL
IS NULL, IS NOT NULL 	Criteria::ISNULL, Criteria::ISNOTNULL
LIKE, ILIKE 	Criteria::LIKE, Criteria::ILIKE
IN, NOT IN 	Criteria::IN, Criteria::NOT_IN
Other SQL Keywords
ORDER BY column ASC 	->addAscendingOrderByColumn(column);
ORDER BY column DESC 	->addDescendingOrderByColumn(column);
LIMIT limit 	->setLimit(limit)
OFFSET offset 	->setOffset(offset)
FROM table1, table2 WHERE table1.col1 = table2.col2 	->addJoin(col1, col2)
FROM table1 LEFT JOIN table2 ON table1.col1 = table2.col2 	->addJoin(col1, col2, Criteria::LEFT_JOIN)
FROM table1 RIGHT JOIN table2 ON table1.col1 = table2.col2 	->addJoin(col1, col2, Criteria::RIGHT_JOIN)

使用或语句查询的例子:
<?php
  $c = new Criteria();
  $c1 = $c->getNewCriterion(TableAPeer::COLUMN_A, 1);
  $c2 = $c->getNewCriterion(TableAPeer::COLUMN_A, 2);
  $c1->addOr($c2);
  $c->add(TableAPeer::COLUMN_B, ‘xxx’);
  $c->add($c1);
?>
等价于
SELECT * FROM table_a WHERE (column_a = 1 OR column_a = 3) AND column_c = ‘xxx

使用group语句查询的例子:
<?php
 $c=new Criteria();
 $c->addSelectColumn('SUM(score) as A');
 $c->addSelectColumn(BuryPeer::SCORE);
 $c->addGroupByColumn('article_id');
 $crit=$c->getNewCriterion(BuryPeer::ARTICLE_ID,$article->getId());
 $c->addHaving($crit);
 $buries=BuryPeer::doSelectRS($c);
?>
等价于select SUM(score) from bury group by article_id having article_id=@ARTICLE_ID

Criteria可以用doSelect,doUpdate,doInsert,doDelete方法.
例:
doDelete($obj)操作
<?php
  ShopTagPeer::doDelete($tag);
?>
doInsert操作
<?php
  $b = new Criteria();
  $b->add(ShopTagPeer::SHOP_ID, $shop_id);
  $b->add(ShopTagPeer::TAG_ID, $tag);
  ShopTagPeer::doInsert($b);
?>
还有一个查询不重复的方法
<?php
$c = new Criteria();
$c->setDistinct();
?>

<?php
//新建
$user = new User();
$user->setName('symfony');
$user->setAge(3);
$user->save();

//查询
$user = UserPeer::retrieveByPk(1);
echo $user->getName() . '   ' . $user->getAge() . '<br />    ';
$users = UserPeer::retrieveByPks(array(1, 2, 3, 4));
foreach ($users as $user) {
echo $user->getName() . '   ' . $user->getAge() . '<br />    ';
}

// 更新
$user = UserPeer::retrieveByPk(1);
$user->setName('new Name');
$user->save();

// 删除
UserPeer::doDelete(1);
$user = UserPeer::retrieveByPk(1);
$user->delete();
?>
<?php
// 查询 name = 'symfony' 的记录
$c = new Criteria();
$c->add(UserPeer::NAME, 'symfony');
$users = UserPeer::doSelect($c);

//只查询一条记录
$user = UserPeer::doSelectOne($c);

//...也支持 group by、limit、table join 等等特性
?>

使用 Criteria::CUSTOM 可使 Criteria 实现自定义条件查询

SELECT *
FROM users
WHERE CONCAT(first_name, ' ', last_name) = '$username';

<?php
$c = new Criteria();
$c->add(UserPeer::FIRST_NAME,
"CONCAT(" . UserPeer::FIRST_NAME . ", ' ', " . UserPeer::LAST_NAME . ") = '" . $username . "'",
Criteria::CUSTOM);
$users = UserPeer::doSelect($c);
?>

当遇到复杂语句查询的时候,可以采取以下方法:
<?php
 $connection = Propel::getConnection();
 $query = 'SELECT MAX(%s) AS max FROM %s'; //写你要查询的sql
 $query = sprintf($query, ArticlePeer::CREATED_AT, ArticlePeer::TABLE_NAME);
 $statement = $connection->prepare($query);
 $statement->execute();
 $resultset = $statement->fetch(PDO::FETCH_OBJ);
 $max = $resultset->max;
?>

WordPress 所驱动