I have two Models that I would like to merge into one timeline. I have been able to do this by creating a View in mysql that normalizes and unions the tables. I created a Model for this view, NewsFeed
. This works well if I do not want related Comment
model. I have gotten close to this by overriding the getMorphClass
method on the model. This allows me to get the related comments for the pictures, but not the posts, because when getMorphClass
is called the model doesn't have any data.
I am open to any approach on how to solve this, not just the way I am proposing, but I don't want to pull more data than I have to from the database.
NewsFeed
<?php
namespace App\Users;
use App\Pictures\Picture;
use App\Social\Comments\CommentableTrait;
use App\Posts\Post;
use App\Users\User;
use Illuminate\Database\Eloquent\Model;
class UserFeed extends Model
{
use CommentableTrait;
public function user()
{
return $this->belongsTo(User::class);
}
public function getMorphClass(){
if ($this->type == 'post'){
return Post::class;
}
return Picture::class;
}
}
MySQL View
CREATE VIEW
`user_feeds`
AS SELECT
`posts`.`id` AS `id`,
`posts`.`user_id` AS `user_id`,
'post' AS `type`,
NULL AS `name`,
NULL AS `thumbnail`,
`posts`.`body` AS `body`,
`posts`.`updated_at` AS `updated_at`,
`posts`.`created_at` AS `created_at`
FROM
`posts`
UNION SELECT
`pictures`.`id` AS `id`,
`pictures`.`user_id` AS `user_id`,
'picture' AS `type`,
`pictures`.`name` AS `name`,
`pictures`.`thumbnail` AS `thumbnail`,
`pictures`.`description` AS `body`,
`pictures`.`updated_at` AS `updated_at`,
`pictures`.`created_at` AS `created_at`
FROM
`pictures`;
pictures table
id
user_id
title
img
img_width
img_height
img_other
description
created_at
updated_at
posts
id
user_id
title
body
created_at
updated_at
You are really close with your idea to build a view. In fact, if you create an actual table instead of a view, the solution becomes quite simple.
With a 'FeedItem' polymorph object that points to your Post class or Picture class, you can attach the comments directly to the FeedItem with a hasMany relationship.
class FeedItem extends Model {
use CommentableTrait;
public function feedable()
{
return $this->morphTo();
}
}
class Post extends Model {
public function feeditem()
{
return $this->morphOne('FeedItem', 'feedable');
}
}
class Picture extends Model {
public function feeditem()
{
return $this->morphOne('FeedItem', 'feedable');
}
}
This solution may require some refactoring on your forms since you will need to create a FeedItem entry for each Post entry and Picture entry. Event listeners for Picture::created and Post::created should do the trick (http://laravel.com/docs/5.1/eloquent#events).
Once it's set up, you can use:
FeedItem::with('comments')->orderBy('created_at','desc')->paginate(15);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With