How to display results from a HABTM relationship in a view? - cakephp

I have a HABTM relationship between a books table and an authors table, joined with an authors_books table.
However, I can't find a solution to display the fields firstname, lastname and fullname in my view.
book-model:
class Book extends AppModel {
var $name = 'Book';
var $hasAndBelongsToMany = array(
'Author' => array(
'className' => 'Author',
'joinTable' => 'authors_books',
'foreignkey' => 'book_id',
'associatioanForeignKey' => 'author_id'
)
);
author-model:
class Author extends AppModel {
var $name = 'Author';
var $virtualFields = array(
'fullname' => "CONCAT(firstname,' ',lastname)"
);
var $fullname = 'fullname';
var $hasAndBelongsToMany = array(
'Book' => array(
'className' => 'Book',
'joinTable' => 'authors_books',
'foreignkey' => 'author_id',
'associatioanForeignKey' => 'book_id'
)
);
books_controller:
function view($id) {
$this->Book->id = $id;
$this->set('book', $this->Book->read());
}
debug($book) in my wiev.ctp give these results:
Array
(
[Book] => Array
(
[id] => 8
[title] => A Forest of Kings: The Untold Story of the Ancient Maya
)
[Author] => Array
(
[0] => Array
(
[id] => 6
[firstname] => Linda
[lastname] => Schele
[fullname] => Linda Schele
)
[1] => Array
(
[id] => 7
[firstname] => David
[lastname] => Freidel
[fullname] => David Freidel
)
)
)
I belive the [0][1] index in the array are the problem.
I've searched the problem, and found some articles on the subject, but can't get any of the solutions to work.
Any help is greatly appreciated. I am using cakephp 1.3.3
Thanks

You would normally loop over the Author array, since you can have a varying number of authors.
<ul>
<?php foreach($book['Author'] as $author){ ?>
<li><?php print($author['fullname']); ?></li>
<?php } ?>
</ul>

Related

CakePHP HABTM relationship saving new associations

Im having some problems with saving my HABTM relationship for some code I'm working with. I have Podcasts and Categories in a HABTM relationship. Code and problem explanation below
Model/Podcast.php
class Podcast extends AppModel{
public $hasMany = 'Episode';
public $hasAndBelongsToMany = array(
'Category' =>
array(
'className' => 'Category',
'joinTable' => 'categories_podcasts',
'foreignKey' => 'podcast_id',
'associationForeignKey' => 'category_id',
'unique' => false
)
);
}
Model/Category.php
class Category extends AppModel{
public $hasAndBelongsToMany = array(
'Podcast' =>
array(
'className' => 'Podcast',
'joinTable' => 'categories_podcasts',
'foreignKey' => 'category_id',
'associationForeignKey' => 'podcast_id',
'unique' => false
)
);
}
This is what the array I'm passing into saveAll() looks like
Array
(
[Podcast] => Array
(
[name] => Mohr Stories - FakeMustache.com
[description] => Join your host Jay Mohr and a rotating cast of his hilarious friends as they regale you with tales and conversation covering every topic imaginable. You haven't heard the half of it until you've heard... Mohr Stories.
[website] => http://www.FakeMustache.com
)
[Category] => Array
(
[0] => 1
[1] => 2
)
)
Which works fine - the podcast is added and the join table is updated, but I was under the impression that my Category array could look like the following:
[Category] => Array
(
[0] => Array
(
[name] => Test
)
[1] => Array
(
[name] => Test2
)
)
Which would save the new categories "Test" and "Test2" and then update the join table with their newly added ids. Is this not possible, or I am missing some of the picture here?
You do are able to do that, but with another data format in which each record contains the associated data as on the following code that allows to save 2 records :
array{
array{
'Podcast' => array {
'id' => 25
},
'Category' => array {
'name' => 'Test'
}
}
array{
'Podcast' => array {
'id' => 25
},
'Category' => array {
'name' => 'Test2'
}
}
}
Pass this array to the saveAll method on one of your Podcast/Category Models.
$this->Podcast->saveAll($myData);
But take care, as you don't provide any id for the categories, they will be created, even if there are existing Category with the same name.
And if a Podcast with the id 25 doesn't exists, it will be created.

Cakephp many to many recursive

Hy i am trying to create a follow feature to my website so user can follow each other , I am using Cakephp what kind of relation should i use , what should i name the tables.
NB: I created a user table + follow table containing user_id and follower_id !
If you don't need to save any information about the relation, then hasAndBelongsToMany is the natural relation to use in this case.
Try this:
// User Model
var $hasAndBelonsToMany = array(
'Follower' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'associationForeignKey' => 'follower_id'
'joinTable' => 'followers_users'
)
)
then you must create the users table as normal, and a table 'followers_users' with columns: 'id', 'user_id', 'follower_id' (and 'created' and 'updated' if you need them).
EDIT :
To retrieve your data (read here) you do it as usual:
$this->User->find('all', array('conditions' => array('id' => 1)));
Then you'll get an array like:
Array(
[User] => array(
[id] => 1
[name] => xxxx
)
[Follower] => array(
[0] => array(
[id] => 2
[name] => yyyy
)
[1] => array(
[id] => 3
[name] => zzzz
)
)
)
To save your data (read here and here), you need to create an array like:
array(
[User] => array(
[id] => 1
[name] => xxx
)
[Follower] => array(
[0] => array(
[id] => 2
)
[1] => array(
[id] => 3
)
)
)
Explanation of what you are looking / example is right in the book:
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#multiple-relations-to-the-same-model
Similar example per the book:
"It is also possible to create self associations as shown below:"
<?php
class Post extends AppModel {
public $name = 'Post';
public $belongsTo = array(
'Parent' => array(
'className' => 'Post',
'foreignKey' => 'parent_id'
)
);
public $hasMany = array(
'Children' => array(
'className' => 'Post',
'foreignKey' => 'parent_id'
)
);
}

cakephp HABTM association saveAll?

I have two tables and a join table. I tried using the $hasAndBelongsToMany but it didnt work, so instead i create a model for the join table and used.
User hasMany Membership
Membership belongsTo User, Club
Club hasMany Membership.
I have a form that saves to both tables.
function dashboard_add(){
$user = $this->Session->read('User');
$register = false;
if (!empty($this->data)) {
if($this->Club->saveAll($this->data)){
$this->Session->setFlash(__('You have registered your club. You will be contacted soon!', true), 'default', array('class' => 'success'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('There was a problem with your registration. Please, try again.', true), 'default', array('class' => 'warning'));
}
}
if (empty($this->data)) {
$this->data = $this->User->read(null, $user['User']['id']);
}
$this->set(compact('register'));
}
The User model has
var $hasMany = array(
'Membership' => array(
'className' => 'Membership',
'foreignKey' => 'user_id'
)
);
The Membership Model has
var $belongsTo = array('User','Membership');
The Club Model has
var $hasMany = array(
'Membership' => array(
'className' => 'Membership',
'foreignKey' => 'club_id'
),
'Upload' => array(
'className' => 'Upload',
'foreignKey' => 'club_id'
)
);
I don't get any errors. The club table populates, but the membership and user table doesnt insert/update.
UPDATE: debug($this->Club->save($this->data));
Array
(
[User] => Array
(
[id] => 1
[first_name] => this
[last_name] => that
[company_name] => other
[city] => this
[state] => that
[zip] => other
[telephone] => that
[email_address] => this
)
[Club] => Array
(
[address] => fvdfvx
[title] => xcvxcv
[category] => xcvxcv
[modified] => 2012-05-03 06:31:12
)
)
I presume you are using cakePHP 1.3.x. See the documentation to understand how cake saves many models at the same time, you need to generate an array with all models to save.. for example:
Array
(
[Article] => Array
(
[title] => My first article
)
[Comment] => Array
(
[0] => Array
(
[comment] => Comment 1
[user_id] => 1
)
[1] => Array
(
[comment] => Comment 2
[user_id] => 2
)
)
)
http://book.cakephp.org/1.3/view/1031/Saving-Your-Data
Apparently in your $this->data you only have a simple array [User]. and not multiple arrays to save as in the Example.
I hope I have helped.
Regards.

Saving a Join Model

I've been reading the cookbook for a while now and still don't get how I'm supposed to do this:
My original problem was this:
A related Model isn't being validated
From RabidFire's commment:
If you want to count the number of
Category models that a new Post is
associated with (on save), then you
need to do this in the beforeSave
function as I've mentioned. As you've
currently set up your models, you
don't need to use the multiple rule
anywhere. If you really, really want
to validate against a list of Category
IDs for some reason, then create a
join model, and validate category_id
with the multiple rule there.
Now, I have these models and are now validating. The problem now is that data isn't being saved in the Join Table:
class Post extends AppModel {
var $name = 'Post';
var $hasMany = array(
'CategoryPost' => array(
'className' => 'CategoryPost'
)
);
var $belongsTo = array(
'Page' => array(
'className' => 'Page'
)
);
class Category extends AppModel {
var $name = 'Category';
var $hasMany = array(
'CategoryPost' => array(
'className' => 'CategoryPost'
)
);
class CategoryPost extends AppModel {
var $name = 'CategoryPost';
var $validate = array(
'category_id' => array(
'rule' => array('multiple', array('in' => array(1, 2, 3, 4))),
'required' => FALSE,
'message' => 'Please select one, two or three options'
)
);
var $belongsTo = array(
'Post' => array(
'className' => 'Post'
),
'Category' => array(
'className' => 'Category'
)
);
This is the new Form:
<div id="content-wrap">
<div id="main">
<h2>Add Post</h2>
<?php echo $this->Session->flash();?>
<div>
<?php
echo $this->Form->create('Post');
echo $this->Form->input('Post.title');
echo $this->Form->input('CategoryPost.category_id', array('multiple' => 'checkbox'));
echo $this->Form->input('Post.body', array('rows' => '3'));
echo $this->Form->input('Page.meta_keywords');
echo $this->Form->input('Page.meta_description');
echo $this->Form->end('Save Post');
?>
</div>
<!-- main ends -->
</div>
The data I am producing from the form is as follows:
Array
(
[Post] => Array
(
[title] => 1234
[body] =>
1234
)
[CategoryPost] => Array
(
[category_id] => Array
(
[0] => 1
[1] => 2
)
)
[Page] => Array
(
[meta_keywords] => 1234
[meta_description] => 1234
[title] => 1234
[layout] => index
)
)
UPDATE: controller action
//Controller action
function admin_add() {
// pr(Debugger::trace());
$this->set('categories', $this->Post->CategoryPost->Category->find('list'));
if ( ! empty($this->data)) {
$this->data['Page']['title'] = $this->data['Post']['title'];
$this->data['Page']['layout'] = 'index';
debug($this->data);
if ($this->Post->saveAll($this->data)) {
$this->Session->setFlash('Your post has been saved', 'flash_good');
$this->redirect($this->here);
}
}
}
UPDATE #2:
Should I just do this manually?
The problem is that the join tables doesn't have things saved in it. Is there something I'm missing?
removed update #3
Join Table schema:
CREATE TABLE IF NOT EXISTS `category_posts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`category_id` int(11) NOT NULL,
`post_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
);
Update: #4
I will put everything about my application here in the hopes of getting to do what I want to do.
// Post Model
class Post extends AppModel {
var $name = 'Post';
var $hasMany = array(
'CategoryPost' => array(
'className' => 'CategoryPost'
)
);
var $belongsTo = array(
'Page' => array(
'className' => 'Page'
)
);
var $actsAs = array('Containable');
var $virtualFields = array(
'date_posted' => 'DATE_SUB(Post.created, INTERVAL 7 DAY)'
);
var $order = array('Post.modified' => 'desc');
var $validate = array(
'title' => array(
'rule' => 'notEmpty'
),
'body' => array(
'rule' => 'notEmpty'
)
);
function getFeed() {
if ($posts = $this->find('all', array('limit' => 20, 'order' => 'Post.created DESC'))) {
return $posts;
}
return FALSE;
}
function getRecentPosts() {
$conditions = array(
'Post.created < (curdate() + interval 7 day)',
);
return $this->find('all', array('limit' => 8, 'conditions' => $conditions));
}
}
// CategoryPost Model
class CategoryPost extends AppModel {
var $name = 'CategoryPost';
var $validate = array(
'category_id' => array(
'rule' => array('multiple', array('in' => array(1, 2, 3, 4))),
'required' => FALSE,
'message' => 'Please select one, two or three options'
)
);
var $belongsTo = array(
'Post' => array(
'className' => 'Post'
),
'Category' => array(
'className' => 'Category'
)
);
var $actsAs = array('Containable');
}
class Page extends AppModel {
var $name = 'Page';
var $order = array('Page.modified' => 'desc');
var $hasOne = array(
'Post' => array(
'className' => 'Post'
));
var $hasMany = array(
'Snippet' => array(
'className' => 'Snippet'
));
var $validate = array(
'title' => array(
'rule' => 'notEmpty'
),
'uris' => array(
'slugged' => array(
'rule' => '/^[a-z0-9-_]+$/i',
'message' => 'This field should only contain characters, numbers, dashes and underscores'
),
'uniqueUrl' => array(
'rule' => array('uniqueUrl'),
'message' => 'A page has already acquired this url'
)
),
'meta_keywords' => array(
'rule' => 'notEmpty'
),
'meta_description' => array(
'rule' => 'notEmpty'
),
'layout' => array(
'rule' => 'notEmpty'
)
);
}
// Form
<div id="main">
<h2>Add Post</h2>
<?php echo $this->Session->flash();?>
<div>
<?php
echo $this->Form->create('Post');
echo $this->Form->input('Post.title');
echo $this->Form->input('CategoryPost.category_id', array('multiple' => 'checkbox'));
echo $this->Form->input('Post.body', array('rows' => '3'));
echo $this->Form->input('Page.meta_keywords');
echo $this->Form->input('Page.meta_description');
echo $this->Form->end('Save Post');
?>
</div>
<!-- main ends -->
</div>
// Posts#admin_add
function admin_add() {
$this->set('categories', $this->Post->CategoryPost->Category->find('list'));
if ( ! empty($this->data)) {
$this->data['Page']['title'] = $this->data['Post']['title'];
$this->data['Page']['layout'] = 'index';
if ($this->Post->saveAll($this->data, array('validate' => 'first'))) {
$this->Session->setFlash('Your post has been saved', 'flash_good');
$this->redirect(array('action' => 'admin_add'));
}
}
}
// Table structure
CREATE TABLE IF NOT EXISTS `posts` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`page_id` int(11) NOT NULL,
`title` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
`uri` varchar(127) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`body` text COLLATE utf8_unicode_ci,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=163 ;
CREATE TABLE IF NOT EXISTS `pages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`uris` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`meta_keywords` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`meta_description` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`layout` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=164 ;
CREATE TABLE IF NOT EXISTS `category_posts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`category_id` int(11) NOT NULL,
`post_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=36 ;
Your data is not being saved because you're trying to save an array into a field.
[CategoryPost] => Array
(
[category_id] => Array
(
[0] => 1
[1] => 2
)
)
The form of the data should look like this:
[CategoryPost] => Array
(
[0] => Array
(
[category_id] => 1
)
[1] => Array
(
[category_id] => 2
)
)
You could use the following code to do this:
// Correcting data form of CategoryPost
$categoryPosts = array();
foreach ($this->data['CategoryPost']['category_id'] as $categoryId) {
$categoryPost = array(
'category_id' => $categoryId
);
array_push($categoryPosts, $categoryPost);
}
$this->data['CategoryPost'] = $categoryPosts;
This code can be placed in the controller before the saveAll call. If you find that you're using this code in multiple places, you can refactor it into the model's beforeSave:
function beforeSave() {
if (isset($this->data['CategoryPost']['category_id']) && is_array($this->data['CategoryPost']['category_id'])) {
... // above code
}
}
Post the schema of your join table (table name and fields) for a possibly better alternative.
Alright, it took me some brain racking but I finally figured it out. Your last comment kind of put everything in place. Here's the simple fix:
In your view:
echo $this->Form->input('CategoryPost.0.category_id', array('multiple' => 'checkbox'));
This should cause the data to be of the following form:
[CategoryPost] => Array
(
[0] => Array
(
[category_id] => Array
(
[0] => 1
[1] => 2
)
)
)
You need to have it in this form because of the hasMany relationship between Post and CategoryPost - or it won't even validate. After you make this change, the beforeSave function will be called. NOW make the necessary changes to beforeSave to make it work! Debugging $this->data will help. I leave this part to you, cause I have another better alternative:
1) Shift the multiple validation rule to the Post model:
class Post extends AppModel {
...
var $validate = array(
'category_id' => array(
'rule' => array('multiple', array('in' => array(1, 2, 3, 4))),
'required' => false,
'message' => 'Please select one, two or three options'
)
);
...
}
2) Change the view accordingly:
echo $this->Form->input('Post.category_id', array('multiple' => 'checkbox'));
3) Shift the beforeSave to the Post model:
function beforeSave() {
if (isset($this->data['Post']['category_id']) && is_array($this->data['Post']['category_id'])) {
$categoryPosts = array();
foreach ($this->data['Post']['category_id'] as $categoryId) {
$categoryPost = array(
'category_id' => $categoryId
);
array_push($categoryPosts, $categoryPost);
}
$this->data['CategoryPost'] = $categoryPosts;
}
return true;
}
This should keep things nice and smooth. Test out both alternatives and let me know if it works! :D
I think that the element for multiple choice should have name like this:
echo $this->Form->input('Category', array('multiple' => 'checkbox'));
For better results create a backup copy of your view file and recreate it using the console bake script.

How do I Find Categories within in Contained Model in CakePHP

I had a recent question here regarding Finding records via conditions in habtm. Now, I could search for posts within a category I searched for.
Now, my questions is how to I retrieve the categories of each post. Sometimes a post has one or more categories in this query:
$this->set('posts', $this->Category->find(
'first',
array(
'conditions' => array(
'Category.uri' => $uri
),
'contain' => array('Post')
)
));
I'd imagine something like this:
$this->set('posts', $this->Category->find(
'first',
array(
'conditions' => array(
'Category.uri' => $uri
),
'contain' => array('Post' => array(
'contain' => 'Category'
))
)
));
Here's what my models look like.
// Category Model
class Category extends AppModel {
var $name = 'Category';
var $hasAndBelongsToMany = array(
'Post' => array(
'className' => 'Post'
)
);
var $actsAs = array('Containable');
}
// Post Model
class Post extends AppModel {
var $name = 'Post';
var $hasAndBelongsToMany = array(
'Category' => array(
'className' => 'Category'
)
);
var $actsAs = array('Containable');
var $virtualFields = array(
'date_posted' => 'DATE_SUB(Post.created, INTERVAL 7 DAY)'
);
}
A sample data would be like this:
categories
id name
1 holidays
2 destinations
posts
id title
1 I am a post
2 I am another post
categories_posts
post_id category_id
1 1
2 2
2 1
I am retrieving posts from holidays.
Array
(
[Category] => Array
(
[id] => 3
[name] => holidays
[uri] => holidays
[created] => 2010-11-25 20:43:03
[modified] => 2010-11-25 20:43:03
)
[Post] => Array
(
[0] => Array
(
[id] => 1
[title] => I am a post
),
[1] => Array
(
[id] => 2
[title] => I am a another post
)
)
)
The problem is that 1 of the posts are in two categories. I'd like to have that information also available.
Close. Try this:
$this->set('posts', $this->Category->find(
'first',
array(
'conditions' => array(
'Category.uri' => $uri
),
'contain' => array('Post' => array('Category'))
)
));
Tangent
Writing Controller code like this has problems:
It will bloat your controller methods and make your controllers less readable.
It prevents you from re-using business logic among your different controllers.
You can satisfactorily correct these problems by moving your find call to a model method:
// Categories Controller
$this->set('posts', $this->Category->get_posts_with_cats($uri));
// Category Model
function get_posts_with_cats($uri) {
$this->find('first', array(
'conditions' => array('Category.uri' => $uri),
'contain' => array('Post' => array('Category'))
));
}
This makes your code More Awesome:
The controller is nice and Skinny, and super readable.
Now you can call the same method from any associated model. So, for instance, in your PostsController you could call: $this->Post->Category->get_posts_with_cats($uri);. Your code is now DRY.
Tangent #2
After you learn how to nest containable models like I've shown you, you may be tempted in the future to do something like this:
$this->find('first', array(
'conditions' => array('Category.uri' => $uri),
'contain' => array(
'Post' => array(
'Category' => array(
'Tag' => array('Author')
)
)
)
));
The good news? You will get the desired model data from the above method (as long as you've defined all of these relationships).
The terrible news? CakePHP doesn't know how to optimize the queries for deep associations like this. So, as your database grows, you'll end up with dozens (if not hundreds (if not thousands)) of SELECT queries to the database, effectively bringing your application to a crawl (if not a halt.)
The good folk that design CakePHP are working on a better containable behavior for CakePHP 2.0, but you'll have to wait until then to get this level of precision.
I'm leaving my other answer as-is because it contains a lot of good info for those who may stumble upon it. As far as solving your problem more elegantly? You'll need to flip the find on it's head and search from the Post model with an ad-hoc join, like this:
// From the CategoriesController
$this->Category->Post->find('all', array(
'joins' => array(
array(
'table' => 'categories_posts',
'alias' => 'CategoryFilter',
'type' => 'inner',
'conditions' => array(
'CategoryFilter.post_id = Post.id'
)
),
array(
'table' => 'categories',
'alias' => 'CategoryUriFilter',
'type' => 'inner',
'conditions' => array(
'CategoryUriFilter.id = CategoryFilter.category_id'
)
)
),
'conditions' => array(
'CategoryUriFilter.uri' => $uri
),
'contain' => array('Category')
));
The array returned will be formatted a bit differently, but it should contain all of the data you were looking for.
I actually did this to get what I want:
function getPostsFromCategory($uri) {
return $this->populatePosts($this->find(
'first',
array(
'conditions' => array(
'Category.uri' => $uri
),
'contain' => array('Post' => array('Category'))
)
));
}
function populatePosts($categoryPosts) {
$posts = $categoryPosts['Post'];
$count = count($categoryPosts['Post']);
for ($i = 0; $i < $count; $i++) {
$categories = $this->Post->find('first', array('conditions' => array('Post.id' => $categoryPosts['Post'][$i]['id']),
'contains' => array('Category')));
// unset($categories['Post']);
foreach ($categories['Category'] as $cat) {
$categoryPosts['Post'][$i]['Categories'][] = $cat;
}
unset($categories);
}
return $categoryPosts;
}
I really don't know if this can be done in a more CakePHP-y way though:
I returns something like this:
Array
(
[Category] => Array
(
[id] => 3
[name] => festivals
[uri] => festivals
[created] => 2010-11-25 20:43:03
[modified] => 2010-11-25 20:43:03
)
[Post] => Array
(
[0] => Array
(
[id] => 28
[title] => A Post
[Categories] => Array
(
[0] => Array
(
[id] => 3
[name] => festivals
[uri] => festivals
[created] => 2010-11-25 20:43:03
[modified] => 2010-11-25 20:43:03
)
[1] => Array
(
[id] => 4
[name] => destinations
[uri] => destinations
[created] => 2010-11-28 13:04:52
[modified] => 2010-11-28 13:04:52
)
)
)
)

Resources