Les Criteria c'est trop nul :p
By eNk` on Thursday, July 9 2009, 13:35 - Symfony - Permalink
Derrière ce titre un peu "trollesque" se cache en fait un petit billet dans lequel nous allons voir comment faire des requêtes avec Propel 1.3 en utilisant directement PDO, donc sans passer par la case Criteria/Criterion, et hydrater les objets correspondant.
Prenons un exemple avec le schéma suivant, dans lequel nous avons des utilisateurs pouvant écrire des posts et faire parti de groupes.
propel:
user:
_attributes: { phpName: User }
id: ~
login: { type: varchar, size: 255, required: true, index: unique }
email: { type: varchar, size: 255 }
created_at: ~
updated_at: ~
post:
_attributes: { phpName: Post }
id: ~
content: { type: longvarchar }
user_id: { type: integer, foreignTable: user, foreignReference: id, onDelete: cascade }
created_at: ~
updated_at: ~
group:
_attributes: { phpName: Group }
id: ~
name: { type: varchar, size: 255 }
created_at: ~
updated_at: ~
user_group:
_attributes: { phpName: UserGroup }
group_id: { type: integer, required: true, primaryKey: true, foreignTable: group, foreignReference: id, onDelete: cascade }
user_id: { type: integer, required: true, primaryKey: true, foreignTable: user, foreignReference: id, onDelete: cascade }
created_at: ~
updated_at: ~
Commençons par voir un exemple très simple avec une requête sur une seule table. Nous allons donc créer une méthode getAll() dans UserPeer pour récupérer tous les utilisateurs. Pour réaliser cela c'est très simple il suffit de récupérer la connexion, préparer un statment avec notre requête SQL, l'exécuter et hydrater les objets à l'aide de le méthode populateObjects() de la classe BaseUserPeer. Au passage toutes les classes BaseXXXPeer générées par Propel possèdent une méthode populateObjects(PDOStament $stmt) permettant d'hydrater des objets de la classe XXX.
/**
* Get all users.
*
* @return array - An array of User objects
*/
public static function getAll()
{
$result = null;
// query
$sql = "SELECT * FROM user ORDER BY user.CREATED_AT DESC";
// get the connexion
$con = Propel::getConnection();
// prepare a statment and execute the query
$pdoStmt = $con->prepare($sql);
$pdoStmt->execute();
// hydrate objects from the resultset
$result = UserPeer::populateObjects($pdoStmt);
return $result;
}
Voyons maintenant un exemple de requête avec une jointure entre les tables user et post afin de sélectionner un utilisateur avec tous ses posts. Nous allons donc créer une méthode getUserByIdWithPost() dans laquelle, comme pour getAll(), nous allons créer un statment, l'exécuter puis hydrater le résultat. Cependant il serait pratique d'hydrater les objets User et Post en même temps et de les lier. Donc au lieu d'utiliser la méthode populateObjects() nous allons créer une méthode populateObjectsWithPost(). Cette méthode va évidement faire le même traitement que populateObjects() et va en plus hydrater les objets Post puis les relier à l'objet User.
Pour hydrater un objet avec Propel il suffit d'utiliser les méthodes suivantes des objets BaseXXXPeer:
- getPrimaryKeyHashFromRow(): donne la clé primaire d'un objet sous forme d'une chaine de caractères unique.
- getInstanceFromPool(): retourne un objet (ou null) présent dans le pool d'instance en fonction de sa clé (généré par getPrimaryKeyHashFromRow() ).
- addInstanceToPool(): ajoute un objet au pool d'instance.
ainsi que la méthode hydrate() des objets BaseXXX qui permet d'hydrater un objet.
/**
* Get a user with all post.
*
* @param int $userId - The user id.
* @return User
*/
public static function getUserByIdWithPost($userId)
{
$user = null;
// query
$sql = "SELECT * FROM user LEFT JOIN post ON user.ID = post.USER_ID WHERE user.ID = :user_id";
// get the connexion
$con = Propel::getConnection();
// prepare a statment and execute the query
$pdoStmt = $con->prepare($sql);
$pdoStmt->bindValue('user_id', $userId, PDO::PARAM_INT);
$pdoStmt->execute();
// hydrate objects from the resultset
$result = UserPeer::populateObjectsWithPost($pdoStmt);
if(isset($result[0])) { $user = $result[0]; }
return $user;
}
/**
* Hydrate some User objects with their related Post.
*
* @param PDOStatement $pdoStmt - A statment PDO.
* @return array
*/
public static function populateObjectsWithPost(PDOStatement $pdoStmt)
{
$results = array();
// set the class once to avoid overhead in the loop
$cls = UserPeer::getOMClass();
$cls = substr('.'.$cls, strrpos('.'.$cls, '.') + 1);
// populate the object(s)
while ($row = $pdoStmt->fetch(PDO::FETCH_NUM))
{
$key = UserPeer::getPrimaryKeyHashFromRow($row, 0);
if (null !== ($obj = UserPeer::getInstanceFromPool($key)))
{
$nextColToStart = UserPeer::NUM_COLUMNS - UserPeer::NUM_LAZY_LOAD_COLUMNS;
}
else
{
$obj = new $cls();
$nextColToStart = $obj->hydrate($row);
UserPeer::addInstanceToPool($obj, $key);
} // if key exists
// hydrate a post object and associate it to the user
if(isset($row[$nextColToStart]))
{
$postKey = PostPeer::getPrimaryKeyHashFromRow($row, $nextColToStart);
if(null === ($post = PostPeer::getInstanceFromPool($postKey)))
{
$post = new Post();
$post->hydrate($row, $nextColToStart);
PostPeer::addInstanceToPool($post, $postKey);
}
$obj->addPost($post);
}
// add the user only if not already added.
if(in_array($obj, $results) === false) { $results[] = $obj; }
}
$pdoStmt->closeCursor();
return $results;
}
Voici maintenant un exemple avec une requête pour récupérer un utilisateur avec tous ses groupes, donc avec une relation n:m entre user et group. Rien de nouveau au niveau de la préparation et exécution du statment. Et au niveau de l'hydratation des objets il faut relier les objets Group à l'objet UserGroup correspondant qui va lui même être rattaché à un objet User.
/**
* Get a user with all related groups
*
* @param int $userId - The user id.
* @return User
*/
public static function getUserByIdWithGroups($userId)
{
$user = null;
// query
$sql = "SELECT user.*, group.*, user_group.*";
$sql .= " FROM user LEFT JOIN user_group ON user.ID = user_group.USER_ID";
$sql .= " LEFT JOIN group ON user_group.GROUP_ID = group.ID";
$sql .= " WHERE user.ID = :user_id";
// get the connexion
$con = Propel::getConnection();
// prepare a statment and execute the query
$pdoStmt = $con->prepare($sql);
$pdoStmt->bindValue('user_id', $userId, PDO::PARAM_INT);
$pdoStmt->execute();
// hydrate objects from the resultset
$result = UserPeer::populateObjectsWithGroups($pdoStmt);
if(isset($result[0])) { $user = $result[0]; }
return $user;
}
/**
* Hydrate some User objects with their related Group.
*
* @param PDOStatement $pdoStmt - A statment PDO.
* @return array
*/
public static function populateObjectsWithUserGroups(PDOStatement $pdoStmt)
{
$results = array();
// set the class once to avoid overhead in the loop
$cls = UserPeer::getOMClass();
$cls = substr('.'.$cls, strrpos('.'.$cls, '.') + 1);
// populate the object(s)
while ($row = $pdoStmt->fetch(PDO::FETCH_NUM))
{
$key = UserPeer::getPrimaryKeyHashFromRow($row, 0);
if (null !== ($obj = UserPeer::getInstanceFromPool($key)))
{
$nextColToStart = UserPeer::NUM_COLUMNS - UserPeer::NUM_LAZY_LOAD_COLUMNS;
}
else
{
$obj = new $cls();
$nextColToStart = $obj->hydrate($row);
UserPeer::addInstanceToPool($obj, $key);
} // if key exists
// hydrate a group object
if(isset($row[$nextColToStart]))
{
$groupKey = GroupPeer::getPrimaryKeyHashFromRow($row, $nextColToStart);
if(null === ($group = GroupPeer::getInstanceFromPool($groupKey)))
{
$group = new Group();
$group->hydrate($row, $nextColToStart);
GroupPeer::addInstanceToPool($group, $groupKey);
}
}
$nextColToStart += GroupPeer::NUM_COLUMNS - GroupPeer::NUM_LAZY_LOAD_COLUMNS;
// hydrate a userGroup object
if(isset($row[$nextColToStart]))
{
$userGroupKey = UserGroupPeer::getPrimaryKeyHashFromRow($row, $nextColToStart);
if(null === ($userGroup = UserGroupPeer::getInstanceFromPool($userGroupKey)))
{
$userGroup = new UserGroup();
$userGroup->hydrate($row, $nextColToStart);
UserGroupPeer::addInstanceToPool($userGroup, $userGroupKey);
}
}
if(isset($userGroup, $group))
{
$userGroup->setGroup($group);
$obj->addUserGroup($userGroup);
}
// add the user only if not already added.
if(in_array($obj, $results) === false) { $results[] = $obj; }
}
$pdoStmt->closeCursor();
return $results;
}
Nous avons créé dans cette exemple les méthodes populateObjectsWithUserGroups() et populateObjectsWithPost() pour hydrater le résultat des requêtes. On remarque que le mécanisme d'hydratation des objets est le même pour toutes les classes générées par Propel, on peut donc factoriser du code afin de ne pas écrire des méthodes populateObjectsXXXXX() à ralonge.
Note: dans la méthode hydrateObject() qui va permettre d'hydrater des objets Propel, j'appelle une méthode getPeerClass() sur la classe $class (dans notre exemple la variable $class peut être égale à User, Post, Group ou encore UserGroup). getPeerClass() est une méthode public static qui retourne le nom de la classe peer associée (par défaut on va retourner la constante PEER des classes BaseXXX).
// getPeerClass method exemple
public static function getPeerClass()
{
return parent::PEER;
}
// class with useful methods.
class PropelTools
{
/**
* Hydrate a $class object from a result row of a result set and link this objet to his parent.
*
* @param array $row
* @param int $startCol
* @param string $class
* @param BaseObject $parentObject [optionnal]
* @param string $addMethod [optionnal]
* @return Object
*/
public static function hydrateObject($row, $startCol, $class, $parentObject = null, $addMethod = null)
{
$myObject = null;
// get peer class name
$peerClass = call_user_func(array($class, 'getPeerClass'));
if(isset($row[$startCol]) && $peerClass !== false)
{
$myObjectKey = call_user_func(array($peerClass, 'getPrimaryKeyHashFromRow'), $row, $startCol);
if( null === ($myObject = call_user_func(array($peerClass, 'getInstanceFromPool'), $myObjectKey)) )
{
$myObject = new $class();
$myObject->hydrate($row, $startCol);
call_user_func(array($classPeer, 'addInstanceToPool'), $myObject, $myObjectKey);
}
if(isset($parentObject, $addMethod))
{
if(is_callable(array($parentObject, $addMethod), true)) { $parentObject->$addMethod($myObject); }
else { throw new sfException(sprintf("Can't call method %s() on parent object.", $addMethod)); }
}
}
return $myObject;
}
}
Et voici ce que donnerai la méthode populateObjectsWithUserGroups() en utilisant la méthode hydrateObject().
public static function populateObjectsWithUserGroups(PDOStatement $pdoStmt)
{
$results = array();
// set the class once to avoid overhead in the loop
$cls = UserPeer::getOMClass();
$cls = substr('.'.$cls, strrpos('.'.$cls, '.') + 1);
// populate the object(s)
while ($row = $pdoStmt->fetch(PDO::FETCH_NUM))
{
$key = UserPeer::getPrimaryKeyHashFromRow($row, 0);
if (null !== ($obj = UserPeer::getInstanceFromPool($key)))
{
$nextColToStart = UserPeer::NUM_COLUMNS - UserPeer::NUM_LAZY_LOAD_COLUMNS;
}
else
{
$obj = new $cls();
$nextColToStart = $obj->hydrate($row);
UserPeer::addInstanceToPool($obj, $key);
} // if key exists
// hydrate a userGroup and group object
$group = PropelTools::hydrateObject($row, $nextColToStart, 'Group', $userGroup, 'setGroup');
$nextColToStart += GroupPeer::NUM_COLUMNS - GroupPeer::NUM_LAZY_LOAD_COLUMNS;
$userGroup = PropelTools::hydrateObject($row, $startCol, 'UserGroup', $obj, 'addUserGroup');
// add the user only if not already added.
if(in_array($obj, $results) === false) { $results[] = $obj; }
}
$pdoStmt->closeCursor();
return $results;
}
Liens utiles:
Comments
Plutôt que d'hydrater les objets Propel avec PDO lors de jointures, je propose :
http://www.symfony-project.org/plug...
... qui devrait déjà faire tout ce qu'il faut.
<A href="http://www.toptoys2trade.com/zhu-zh... ">zhu zhu pets</A>
<a href="http://www.toptoys2trade.com/a-sill... "> silly bandz</a>
<A href="http://www.toptoys2trade.com/power-... "> power balance</A>
<A href="http://www.toptoys2trade.com/animal... ">animal rubber bands</A>
<A href="http://www.toptoys2trade.com/baby-c... ">Baby Carriers</A>
<A href="http://www.toptoys2trade.com/pillow... ">Pillow Pets</A>
<A href="http://www.toptoys2trade.com/plush-... ">Plush Pencil Case</A>
[url=http://www.b2chandbag.com/guess-han... ]guess handbags[/url]
[url=http://www.b2chandbag.com/d&g-h... ]d&g handbags[/url]
[url=http://www.b2chandbag.com/ ]prada handbags[/url]
[url=http://www.topcasualshoes.com/ ]ecco shoes[/url]
The loans suppose to be essential for people, which are willing to start their own company. In fact, this is very comfortable to get a collateral loan.