Permalink
Browse files

add One-to-Many Relationships in Databases

  • Loading branch information...
1 parent c10a97e commit 78232c0530c5d1ecdf0f4b19be47656521aad832 @gjerokrsteski committed Nov 23, 2011
View
@@ -1,7 +1,7 @@
<?php
set_include_path(
dirname(dirname(__FILE__))
- .'/model-stock-portfolio'
+ .'/php-identity-map'
.PATH_SEPARATOR
.dirname(__FILE__)
.PATH_SEPARATOR
View
@@ -0,0 +1,18 @@
+CREATE TABLE IF NOT EXISTS tbl_article (
+
+ id INT NOT NULL AUTO_INCREMENT,
+ title VARCHAR(30),
+ content VARCHAR(255),
+ userId INT NOT NULL,
+
+ PRIMARY KEY (id),
+
+ INDEX (userId),
+
+ FOREIGN KEY (userId)
+
+ REFERENCES tbl_user (id)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE
+
+) TYPE=INNODB DEFAULT CHARSET=utf8;
View
@@ -1,6 +1,9 @@
-CREATE TABLE IF NOT EXISTS `tbl_user` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `nickname` varchar(10) CHARACTER SET latin1 NOT NULL,
- `password` varchar(50) CHARACTER SET latin1 NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+CREATE TABLE IF NOT EXISTS tbl_user (
+
+ id INT NOT NULL AUTO_INCREMENT,
+ nickname VARCHAR(30) NOT NULL,
+ password VARCHAR(30) NOT NULL,
+
+ PRIMARY KEY (id)
+
+) TYPE=INNODB DEFAULT CHARSET=utf8;
View
@@ -0,0 +1,73 @@
+<?php
+class Article
+{
+ protected $title;
+ protected $content;
+
+ /**
+ * User has many Articles.
+ * @var User
+ */
+ protected $user;
+
+ public function __construct($title = null, $content = null, User $user = null)
+ {
+ $this->title = $title;
+ $this->content = $content;
+ $this->user = $user;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * @param string $title
+ * @return Article
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getContent()
+ {
+ return $this->content;
+ }
+
+ /**
+ * @param string $content
+ * @return Article
+ */
+ public function setContent($content)
+ {
+ $this->content = $content;
+ return $this;
+ }
+
+ /**
+ * @return User
+ */
+ public function getUser()
+ {
+ return $this->user;
+ }
+
+ /**
+ * @param User $user
+ * @return Article
+ */
+ public function setUser(User $user)
+ {
+ $this->user = $user;
+ return $this;
+ }
+}
View
@@ -1,9 +1,15 @@
<?php
class User
{
+ protected $id;
protected $nickname;
protected $password;
-
+
+ /**
+ * @var array
+ */
+ protected $articles;
+
/**
* @param string $nickname
* @param string $password
@@ -51,4 +57,30 @@ public function setPassword($password)
return $this;
}
+
+ /**
+ * @return integer The unique id.
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ public function addArticle($title, $content)
+ {
+ $this->articles[] = new Article($title, $content, $this);
+ }
+
+ public function getArticles()
+ {
+ $this->articles;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function hasArticles()
+ {
+ return (true === is_array($this->articles) && false === empty($this->articles));
+ }
}
@@ -0,0 +1,28 @@
+<?php
+abstract class AbstractMapper implements MapperInterface
+{
+ /**
+ * @var PDO The database resource.
+ */
+ protected $db;
+
+ /**
+ * @var IdentityMap
+ */
+ protected $identityMap;
+
+ /**
+ * @param PDO $db
+ */
+ public function __construct(PDO $db)
+ {
+ $this->db = $db;
+ $this->identityMap = new IdentityMap();
+ }
+
+ /**
+ * @param integer $id
+ * @return array
+ */
+ abstract public function find($id);
+}
@@ -0,0 +1,108 @@
+<?php
+class ArticleMapper extends AbstractMapper
+{
+ /**
+ * @param integer $id
+ * @throws OutOfBoundsException
+ * @return Article
+ */
+ public function find($id)
+ {
+ if (true === $this->identityMap->hasId($id))
+ {
+ return $this->identityMap->getObject($id);
+ }
+
+ $sth = $this->db->prepare('SELECT * FROM tbl_article WHERE id = :id');
+ $sth->bindValue(':id', $id, PDO::PARAM_INT);
+ $sth->execute();
+
+ if ($sth->rowCount() == 0)
+ {
+ throw new OutOfBoundsException(
+ sprintf('No article with id #%d exists.', $id)
+ );
+ }
+
+ $object = $sth->fetchObject();
+
+ $userMapper = new UserMapper($this->db);
+ $article = new Article($object->title, $object->content);
+
+ $article->setUser($userMapper->find($object->userId));
+
+ $this->identityMap->set($id, $article);
+
+ return $article;
+ }
+
+ /**
+ * @param Article $article
+ * @throws MapperException
+ * @return integer A lastInsertId.
+ */
+ public function insert(Article $article)
+ {
+ if (true === $this->identityMap->hasObject($article))
+ {
+ throw new MapperException('Object has an ID, cannot insert.');
+ }
+
+ $sth = $this->db->prepare(
+ "INSERT INTO tbl_article (title, content, userId) VALUES (:title, :content, :userId)"
+ );
+
+ $sth->bindValue(':title', $article->getTitle(), PDO::PARAM_STR);
+ $sth->bindValue(':content', $article->getContent(), PDO::PARAM_STR);
+ $sth->bindValue(':userId', $article->getUser()->getId(), PDO::PARAM_INT);
+ $sth->execute();
+
+ $this->identityMap->set((int)$this->db->lastInsertId(), $article);
+
+ return (int) $this->db->lastInsertId();
+ }
+
+ /**
+ * @param Article $article
+ * @throws MapperException
+ * @return boolean
+ */
+ public function update(Article $article)
+ {
+ if (false === $this->identityMap->hasObject($article))
+ {
+ throw new MapperException('Object has no ID, cannot update.');
+ }
+
+ $sth = $this->db->prepare(
+ "UPDATE tbl_article SET title = :title, content = :content WHERE id = :id"
+ );
+
+ $sth->bindValue(':title', $article->getTitle(), PDO::PARAM_STR);
+ $sth->bindValue(':content', $article->getContent(), PDO::PARAM_STR);
+ $sth->bindValue(':id', $this->identityMap->getId($article), PDO::PARAM_INT);
+
+ return $sth->execute();
+ }
+
+ /**
+ * @param Article $article
+ * @throws MapperException
+ * @return boolean
+ */
+ public function delete(Article $article)
+ {
+ if (false === $this->identityMap->hasObject($article))
+ {
+ throw new MapperException('Object has no ID, cannot delete.');
+ }
+
+ $sth = $this->db->prepare(
+ "DELETE FROM tbl_article WHERE id = :id LIMIT 1"
+ );
+
+ $sth->bindValue(':id', $this->identityMap->getId($article), PDO::PARAM_INT);
+
+ return $sth->execute();
+ }
+}
@@ -5,10 +5,4 @@
* @param PDO $db
*/
public function __construct(PDO $db);
-
- /**
- * @param integer $id
- * @return array
- */
- public function find($id);
}
@@ -1,26 +1,7 @@
<?php
-class UserMapper implements MapperInterface
+class UserMapper extends AbstractMapper
{
/**
- * @var PDO The database resource.
- */
- protected $db;
-
- /**
- * @var IdentityMap
- */
- protected $identityMap;
-
- /**
- * @param PDO $db
- */
- public function __construct(PDO $db)
- {
- $this->db = $db;
- $this->identityMap = new IdentityMap();
- }
-
- /**
* @param integer $id
* @throws OutOfBoundsException
* @return User
@@ -46,6 +27,10 @@ public function find($id)
$object = $sth->fetchObject();
$user = new User($object->nickname, $object->password);
+ $attribute = new ReflectionProperty($user, 'id');
+ $attribute->setAccessible(true);
+ $attribute->setValue($user, $id);
+
$this->identityMap->set($id, $user);
return $user;
@@ -64,14 +49,29 @@ public function insert(User $user)
}
$sth = $this->db->prepare(
- "INSERT INTO tbl_user (nickname, `password`) VALUES(':nick', ':passwd')"
+ "INSERT INTO tbl_user (nickname, `password`) VALUES (:nick, :passwd)"
);
$sth->bindValue(':nick', $user->getNickname(), PDO::PARAM_STR);
$sth->bindValue(':passwd', $user->getPassword(), PDO::PARAM_STR);
$sth->execute();
- $this->identityMap->set((int)$this->db->lastInsertId(), $user);
+ $id = (int)$this->db->lastInsertId();
+
+ $attribute = new ReflectionProperty($user, 'id');
+ $attribute->setAccessible(true);
+ $attribute->setValue($user, $id);
+
+ if (true === $user->hasArticles())
+ {
+ $articleMapper = new ArticleMapper($this->db);
+
+ foreach ($user->getArticles() as $article) {
+ $articleMapper->insert($article);
+ }
+ }
+
+ $this->identityMap->set($id, $user);
return (int) $this->db->lastInsertId();
}
Oops, something went wrong.

0 comments on commit 78232c0

Please sign in to comment.