I am having articles and features tables and a pivot article_feature table. Features table have items like commenting, like, share. I also have tables comments and likes .
In my function I should get all features for an article and then get all the results from relationships. Basically tell laravel get me all features and for each feature give me all results. The problem is that features table is not connected with tables like comments and likes. I was wondering if that is possible with hasManyThrough but not sure how to do it since there could be many features. I have set up relationships in Article model like this:
public function comments()
{
return $this->hasMany('App\Comment');
}
public function features()
{
return $this->belongsToMany('App\Feature');
}
public function likes()
{
return $this->hasMany('App\Like');
}
This is my function:
public function latest(){
$result = Article::with('comments.user')->where('publish', 1)->orderBy('created_at', 'desc')->paginate(15);
$user = JWTAuth::parseToken()->authenticate();
foreach($result as $article){
$articles[$article->id] = $article;
$articleFeatures = $article->features()->get()->toArray();
if (count($articleFeatures) != 0) {
$articles[$article->id]['features'] = $articleFeatures;
$articles[$article->id]['likes'] = $article->likes()->get();
$articles[$article->id]['comments'] = $article->comments()->get();
}
return $articles;
}
Related
I am trying to create Laravel/Vue project with two models: Category and Article. Vue part haves tree-view, which will display categories and articles tree. Categories may belong to another categories, Article may belong only to Article.
How can i form json tree from these relations?
model Category
public function articles() {
return $this->hasMany(Article::class);
}
public function childs() {
return $this->hasMany(Category::class)->union($this->files()->toBase());
}
but it shows The used SELECT statements have a different number of columns, because there is defferent fields in results.
One solution i see here is to find every article and post and create array, then jsonify it. Maybe any better solutions?
UPDATE
Done it with this code (in api controller):
public function nodes() {
$rootCategories = Category::where('category_id', null)->get();
$out = $this->_nodes($rootCategories);
return response()->json($out);
}
private function _nodes($eCategories) {
$out = [];
foreach($eCategories as $cat) {
$out[$cat->id] = $cat->toArray();
$out[$cat->id]["type"] = "folder";
$out[$cat->id]["childs"] = [];
foreach ($cat->articles as $article) {
$out[$cat->id]["childs"][$article->id] = $article->toArray();
$out[$cat->id]["childs"][$article->id]["type"] = "article";
}
if ($cat->categories) {
$out[$cat->id]["childs"] = $out[$cat->id]["childs"] + $this->_nodesCategory($cat->categories);
}
}
return $out;
}
In my routes/web.php I have a route like this...
Route::get('/tags/{tag}', 'TagsController#show');
Then, inside TagsController because I have a post_tag pivot table that has been defined as a many-to-many relationship.
Tag.php...
public function posts(){
return $this->belongsToMany(Post::class);
}
public function getRouteKeyName(){
return 'name';
}
Post.php...
public function tags(){
return $this->belongsToMany(Tag::class);
}
I get the posts for a certain tag like this...
public function show(Tag $tag){
$posts = $tag->posts;
return view('posts.index', compact('posts','tag'));
}
Then, to sort the posts into newest first I can do this in index.blade.php...
#foreach ($posts->sortByDesc('created_at') as $post)
#include('posts.post')
#endforeach
This works fine, but I'm doing the re-ordering at collection level when I'd prefer to do it at query level.
From Eloquent: Relationships I can see that I can do something like this, which also works...
$user = App\User::find(1);
foreach ($user->roles as $role) {
//
}
But, something like this does not seem to work...
public function show($tag){
$posts = \App\Tag::find($tag);
return view('posts.index', compact('posts'));
}
My question is, how can I filter/order the data at a query level when using pivot tables?
To order your collection you must change
public function tags(){
return $this->belongsToMany(Tag::class);
}
to
public function tags(){
return $this->belongsToMany(Tag::class)->orderBy('created_at');
}
Extending #leli. 1337 answer
To order content without changing the relation created.
First, keep the original relation
class User
{
public function tags
{
return $this->belongsToMany(Tag::class);
}
}
Second, during query building do the following
//say you are doing query building
$users = User::with([
'tags' => function($query) {
$query->orderBy('tags.created_at','desc');
}
])->get();
With this, you can order the content of tags data and in query level also if needed you can add more where clauses to the tags table query builder.
I have 3 models:
class Site extends Model
{
public function users()
{
return $this->belongsToMany('App\Models\User');
}
public function stats()
{
return $this->hasMany('App\Models\Stat');
}
}
class User extends Model
{
public function sites()
{
return $this->belongsToMany('App\Models\Site');
}
public function stats()
{
return $this->belongsToMany('App\Models\Stat');
}
}
class Stat extends Model
{
public function users()
{
return $this->belongsToMany('App\Models\User');
}
public function sites()
{
return $this->belongsTo('App\Models\Site');
}
}
So there are :
a many to many relation between sites and users
a many to many relation between users and stats
a one to many relation between site and stats
A site have a list of stats and from this list, an user can have some stats.
I'm trying to get all sites and foreach site, count of stats for the connected user.
For the moment i tried :
//repository
function getAll($user_id = 0)
{
$with = [];
$with['users'] = function ($query) use ($user_id) {
$query->where('id', '=', $user_id);
};
return Site::with($with)->orderBy('name')->get();
}
//controller
$sites = getAll($user_id);
//view
foreach($sites as $site){
$count_stats = $site->users->first()->stats->where('site_id',$site->id)->count();
}
It works but it is not very elegant, it does a lot of sql requests and the page is slower.
Do you have a better solution ?
If Sites have many Users, and Sites have many Stats, then a User has many Stats through Site
Modify User class:
public function stats() {
return $this->hasManyThrough('App\Stat', 'App\Site', 'user_id', 'site_id');
}
Eager load:
$user = User::with('stats')->find($user_id);
$stats = $user->stats();
Also, I think your Stat should belongsTo Site, since Site hasMany Stat. You need to change a lot of the belongsToMany as well since they look incorrectly used.
I'm still struggeling with the laravel Models. At first I tried doing it all using the tables, but thats not smart, I'll miss out on lots of the laravel functions.
I have the following setup
ProjectTwitterStatus links the projects and the twitter statuses.
TwitterStatus has all the details of a twitter status and has a unique ID ('posted at' datetime of tweet is among the details)
TwitterRetweets has the ID of the TwitterStatus - the actual retweet - and the tweet ID of the retweeted status
TwitterReplies has the ID of the TwitterStatus - that is the actual reply - and/or the user ID if not a reply to a status but to a user.
What I want? To get for each date (DATE(datetime)) the count of the statuses, retweets and replies, using the laravel model relations.
These are the models.
class ProjectTwitterStatus extends Eloquent {
protected $table = 'project_twitter_statuses';
protected $softDelete = true;
public function twitterStatus() {
return $this->belongsTo('TwitterStatus');
}
public function project() {
return $this->belongsTo('Project');
}
}
class TwitterStatus extends Eloquent {
protected $table = 'twitter_statuses';
public function twitterRetweet() {
return $this->hasMany('TwitterRetweet');
}
public function twitterReply() {
return $this->hasMany('TwitterReply');
}
public function twitterUser() {
return $this->belongsTo('TwitterUser');
}
public function projectTwitterStatus() {
return $this->hasMany('ProjectTwitterStatus');
}
}
class TwitterRetweet extends Eloquent {
protected $table = 'twitter_retweets';
public function twitterStatus() {
return $this->belongsTo('TwitterStatus');
}
}
class TwitterReply extends Eloquent {
protected $table = 'twitter_replies';
public function twitterStatus() {
return $this->belongsTo('TwitterStatus');
}
}
I got the count of the twitterStatuses using this:
$twitterStatuses = TwitterStatus::has('projectTwitterStatus')
->groupBy(DB::raw('DATE(datetime)'))
->get(array(DB::raw('COUNT(id) AS tweets'),DB::raw('DATE(datetime) AS date')));
I tried for example this to get the retweet count added but that has no effect (a reference to the model apears in the object -> array().
$twitterStatuses = TwitterStatus::has('projectTwitterStatus')
->with(array('twitterRetweet' => function($query)
{
$query->count();
}))
->groupBy(DB::raw('DATE(datetime)'))
->take(10)
->get(array(DB::raw('COUNT(id) AS tweets'),DB::raw('DATE(datetime) AS date')));
Can anyone point me in the right direction?
Not 100% sure how your intended solution is to be used - Assuming you simply want a count of the number of retweets related to twitterStatus?
$count = $twitterStatus->twitterRetweet()->count();
where $twitterStatus is an already retrieved model - not a collection.
if $twitterStatus is a collection to iterate through you can also eager load the related model using either with() or load()
Then you can iterate through each model in the collection - depends on how you wanted to use the results
I have a model where I'm eager-loading two references to another table (in this case posts.created_by_id == users.id and posts.updated_by_id == users.id).
class Post {
protected $table = 'posts';
public function scopeLatest() {
return $query->with(['created_by', 'updated_by'])
->orderBy('created_at', 'desc');
}
public function createdBy() {
return $this->belongsTo('User');
}
public function updatedBy() {
return $this->belongsTo('User');
}
}
class User {
protected $table = 'users';
public function posts() {
return $this->hasMany('Post', 'created_by');
}
}
This results in something like the following queries:
SELECT * FROM tbl ORDER BY created_at DESC;
SELECT * FROM users WHERE id IN (?); (created_at)
SELECT * FROM users WHERE id IN (?); (updated_at)
This makes sense - we're loading all of the referenced records in created_by, and then updated_by - however we could optimise this to combine the ids make a single query to users.
My question is: is this something Eloquent currently supports?
Didn't work for me. It seems that the eager load works for only one instance of the table. For me only one of the relationships has been filled. Eloquent probably uses the the table name as a pointer for the eager load. It generates all the eager load queries but only one will be filled.
I had to separate the data into different tables just because of this problem (and haven't had the time to dig that deep into Eloquent code.)
I think this is what you might be looking for:
class Post {
protected $table = 'posts';
public function scopeLatest($query) {
return $query->with('createdBy', 'updatedBy')
->orderBy('created_at', 'desc');
}
public function createdBy() {
return $this->belongsTo('User','created_by_id');
}
public function updatedBy() {
return $this->belongsTo('User','updated_by_id');
}
}
class User {
protected $table = 'users';
public function postsCreated() {
return $this->hasMany('Post', 'created_by_id');
}
public function postsUpdated() {
return $this->hasMany('Post', 'updated_by_id');
}
}