Order by activity date instead of users followed - laravel

I have two tables, the activities table and followings table. I want to get "following" activity the date it was created instead of by the date the user was created.
Schema::create('activities', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('user_id')->index();
$table->integer('activity_id')->index();
$table->string('activity_type', 50);
$table->timestamps();
$table->index(['activity_id', 'activity_type']);
});
Schema::create('followings', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('user_id');
$table->unsignedBigInteger('follower_id');
$table->dateTime('followed_date')->nullable();
$table->dateTime('unfollowed_date')->nullable();
$table->timestamps();
$table->unique(['user_id', 'follower_id']);
});
Right now I have this code:
Controller:
$followings = $user->following()->get();
View:
#foreach($followings->sortByDesc('id') as $following)
#foreach($following->user->activity->sortByDesc('id') as $activity)
#if($activity->activity_type == 'App\Thread')
<li class="activity">
<div class="activity-user">
<p class="avatar" style="background-color: {{ $activity->user->color }}">
{{ substr($activity->user->name, 0, 1) }}</p>
</div>
<div class="activity-body">
<h4>{{ $activity->user->name }}
published <a
href="/forums/{{ $activity->activity->channel->slug }}/{{ $activity->activity->slug }}/">
{{ $activity->activity->title }}</a></h4>
<p>
{!! BBCode::parse($activity->activity->body) !!}
</p>
<p class="created-at">
{{-- {{ $activity->activity->created_at->format('l') }}
at
{{ $activity->activity->created_at->format('h:i A') }} --}}
{{ $activity->activity->created_at->format('M j, Y') }}
</p>
</div>
</li>
#endif
#endforeach
#endforeach
Model:
public function following()
{
return $this->hasMany(Following::class);
}
Is there anyway for me to reach the polymorphic table and order by the Activity date instead of by the following() date?
Right now, it returns records based on the user's name in the date they posted it, but I would like it strictly by the activity.

For your scenario, you will be better of with a custom DB query like this, I haven't tested it so you will need to adjust for your use case but basic concept for Laravel is pretty much the same.
$following = Following::join('users', 'followings.user_id', '=','users.id')
->join('activites', 'users.id', '=', 'activities.user_id')
->orderBy('activities.created_at')
->select('followings.*')
->get();

Here's how I did it:
Controller
$followings = $user->following()->get();
foreach($followings as $following) {
foreach($following->user->activity as $activity) {
$followingArray[] = $activity->id;
}
}
//dd($followingArray);
if(!empty($followingArray)){
$following = Activity::whereIn('id', $followingArray)->orderBy('created_at', 'desc')->get();
} else {
$following = '';
}
View:
#if(!empty($following))
#foreach($following as $activity)

Related

I want to displaying comments for each post

hello I just moved from php native and want to try relationships in laravel, so I want to display a post and each comment and the name of user who comments so that when looped the post has a comment that has a relationship with it.
So this is my HomeController.php :
public function index() {
$posts = Post::all();
$comment = Comment::all();
return view('home', [
'posts' => $posts,
'comments' => $comment
]);
}
My Post.php :
public function comments() {
return $this->hasMany(Comment::class);
}
public function user() {
return $this->belongsTo(User::class);
}
My Comments.php :
public function post() {
return $this->belongsTo(Post::class);
}
public function user() {
return $this->belongsTo(User::class);
}
My User.php :
public function post() {
return $this->hasMany(Post::class);
}
My home.blade.php :
#foreach ($posts as $post)
<div class="box-post">
<p>{{ $post->user->name }}</p>
<p>{{ $post->post_content}}</p>
<button>Comment</button>
</div>
#foreach ($comments as $comment)
<div class="box-post">{{ $comment->comment_content }} </div>
#endforeach
#endforeach
the code above runs with the post displayed along with the author but all the comments also appear in all posts, I have searched for references and tried to change the comments value in Controller but the results are still the same so I made the comments appear in each post *$comment = Comment::all();.
what I want is to display posts and comments that relate to each post, like a twitter feature that can reply to people's tweets.
Thx you..
Your relations builded well, so just can simply use $post->comments can get all comments related on each post.
#foreach ($posts as $post)
<div class="box-post">
<p>{{ $post->user->name }}</p>
<p>{{ $post->post_content}}</p>
<button>Comment</button>
</div>
#foreach ($post->comments as $comment) // make changes here
<div class="box-post">{{ $comment->comment_content }} </div>
#endforeach
#endforeach
public function index() {
$posts = Post::with('comments')->get();
return view('home', [
'posts' => $posts,
]);
}
then in blade
#foreach ($posts as $post)
<div class="box-post">
<p>{{ $post->user->name }}</p>
<p>{{ $post->post_content}}</p>
<button>Comment</button>
</div>
#foreach ($post->comments as $comment)
<div class="box-post">{{ $comment->comment_content }} </div>
#endforeach
#endforeach
You have done fine job building models and their relationship.
I recommend you change how you call post query.
public function index(Request $request) {
// add eager loading comments for each post and user
$posts = Post::with('comments', 'user')->get();
// eager loading user is needed, cause in blade you call $post->user->name
// calling comments will no longer needed
// return only $posts
return view('home', compact('posts'));
}
And inside your blade, you can call comments from each post, like this.
#foreach ($posts as $post)
<div class="box-post">
<p>{{ $post->user->name }}</p>
<p>{{ $post->post_content}}</p>
<button>Comment</button>
</div>
#foreach ($post->comments as $comment)
<div class="box-post">{{ $comment->comment_content }} </div>
#endforeach
#endforeach
That's it and if you use barryvdh/laravel-debugbar , you will see there are only 3 queries run, each one from table posts, comments and users.
And for comments query and users query, not all rows will be fetched,
only related to fetched posts from the 1st query.
Have fun using Laravel Framework!
Your PHP native knowledge/expertise will be very useful and speed up your learning/mastering process.

Trying to get property 'image_url' of non-object (View: /app/resources/views/library.blade.php)

My code keeps returning an error when I deploy to google cloud, but this works locally
Trying to get property 'image_url' of non-object (View: /app/resources/views/library.blade.php)
In my view, I have
#forelse($favorites as $fav)
<div class="col-6 col-md-3">
<a href="{{ route('book', $fav->id) }}">
<img src="{{ $fav->book->image_url }}" alt="trending image" />
</a>
<p class="authorone">{{ $fav->book->author->name }}</p>
<h1 class="book-title">{{ $fav->book->name }}</h1>
#empty
<h2>You have no favourites</h2>
<br/><br/>
Return To Discover
</div>
#endforelse
Here is my controller
public function mylibrary(){
// $mybooks = MyBook::where('user_id', Auth::id())->get();
$favorites = Favorite::where('user_id', Auth::id())->get();
return view('library')->with('favorites', $favorites);
}
In my Favorite Model, I have this
public function book ()
{
return $this->belongsTo(Book::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
In my Book Model, I have this
public function favorites()
{
return $this->hasMany(Favorite::class);
}
In my User Model, I have this
public function favorites(){
return $this->hasMany(Favorite::class);
}
My favorites table
public function up()
{
Schema::create('favorites', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('book_id')->unsigned();
$table->integer('user_id')->unsigned();
$table->timestamps();
});
}
I don't know it's not working when I deploy to google cloud, but it works locally. I can't seems to figure out what I am doing wrong.
I also tried this
public function mylibrary(Book $book){
// $mybooks = MyBook::where('user_id', Auth::id())->get();
$favorites = Favorite::where('user_id', Auth::id())->get();
return view('library')->with('favorites', $favorites)->with('book', $book);
}
My Books schema
Schema::create('books', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->text('about');
$table->string('image');
$table->string('image_url');
$table->string('epub_url');
$table->integer('author_id');
$table->string('publisher');
$table->year('year');
$table->boolean('recommended')->default(0);
$table->timestamps();
});
When querying, you may specify which relationships should be eager
loaded using the with method:
Blockquote
public function mylibrary() {
$data = User::with('favorites', 'favorites.book')->find(Auth::id());
return view('library')->with('userFavorites', $data);
}
You may check for null in chained statements using the Null Coalescing Operator ?? ( introduced in PHP 7) :
//...
<img src="{{ $fav->book->image_url ?? '' }}" alt="trending image" />
//...
Edit
#forelse($userFavorites->favorites as $fav)
<div class="col-6 col-md-3">
<a href="{{ route('book', $fav->id) }}">
<img src="{{ $fav->book->image_url ?? '' }}" alt="trending image"/>
</a>
<p class="authorone">{{ $fav->book->author->name ?? '' }}</p>
<h1 class="book-title">{{ $fav->book->name ?? '' }}</h1>
#empty
<h2>You have no favourites</h2>
<br/><br/>
Return To Discover
</div>
#endforelse

Use "where" and "limit" to child in #foreach

I want to display all user's profile into views and they posts. It's pretty simple:
#foreach($profiles as $profile)
{{ $profile->name }}
#foreach($profile->posts as $post)
{{$post->title}}
#endforeach
#endforeach
But I want to display only the latests posts (->orderBy('created_at', 'desc')->limit(4) ) and only accepted posts (->where('accept', 1)). How can I do that?
You already have what you need. You just need to put it all together.
#foreach($profile->posts()->where('accept', 1)->orderBy('created_at', 'desc')->limit(4)->get() as $post)
I would consider creating a query scope on your profile model. That way you can do something like $profile->latestPosts.
Or you can use the relationship to return exactly what you need.
public function latestPosts() {
return $this->posts()->where('accept', 1)->orderBy('created_at', 'desc')->limit(4)->get();
}
Then you could use it as:
#foreach($profile->latestPosts() as $post)
{{$post->title}}
#endforeach
A better solution would be to lazy load the posts you need when loading the profiles in the controller.
$profile = Profile::with(['posts' => function ($post) {
$post->where('accept', 1)->orderBy('created_at', 'desc')->limit(4);
}])->limit(10)->get();
then in the blade you can do the same as before
#foreach($profiles as $profile)
{{ $profile->name }}
#foreach($profile->posts as $post)
{{$post->title}}
#endforeach
#endforeach
if you want to use a scope for latestAccepted, you can on the Post model
class Post extends Model
{
public function scopeLatestAccepted($query)
{
return $query->where('accept', 1)->orderBy('created_at', 'desc')->limit(4)
}
}
Then lazy load it
$profile = Profile::with(['posts' => function ($post) {
$post->latestAccepted();
}])->limit(10)->get();
what you have to do is pretty simple.
create table users
create table post
create a relationships between the 2 tables
write you code in controller to join the table and query all the users post based on his/her username or password
see sample screenshot for table relationships
//Controller
Public function ViewPost(){
$data['data']=DB::table('useratable')
->where('useratable.username','=', $uname)
->where('useratable.password','<=',$pwd)
->leftjoin('posttable', 'useratable.idusertable', '=', 'posttable.userid')
->orderby('posttable.idposttable','desc')
->get();
}
//Route
Route::get('/view-post','YourController#ViewPost')
//view
#foreach($data as $r)
{{ $r->fullname}} <br>
{{ $r->email}} <br>
{{ $r->topic }} <br>
{{ $r->content }} <br>
{{ $r->datetime}} <br>
...
#endforeach
You need some codes like it on controller:
$profiles = Profile::with(['posts' => function ($query) {
$query->where('accept', 1)
->orderBy('created_at', 'desc');
}])->get();
And add this one to blade file:
#foreach($profiles as $profile)
{{ $profile->name }}
#foreach($profile->posts as $post)
{{$post->title}}
#if ($loop->iteration == 4)
#break
#endif
#endforeach
#endforeach

Reorder menu items without affecting database id

I have a menu in my Laravel application which lists tags. I want to be able to add more tags and change their order, but I don't want to affect the underlying database ids for each tag. Because that would break any data I have already associated with them. So I thought I add an extra column to my database called 'display_order' like so:
Database:
Schema::create('tags', function (Blueprint $table) {
$table->increments('id');
$table->integer('display_order');
$table->string('name');
$table->timestamps();
});
Now I want to create a view to let the user change the value of the display_order field. Initially I show a page which prints each tag and its display_order and allows the user to update the value in a text field:
Controller:
public function showTagsOrder()
{
$tags = Tag::all()->sortBy('display_order');
return view('menu.tags', compact('tags'));
}
Route:
Route::get('/menu/tags', ['as' => 'menu_tags', 'uses' => 'MenuOrderController#showTagsOrder']);
View:
{!! Form::open(['action' => 'MenuOrderController#updateTagsOrder']) !!}
#foreach($tags->chunk(18) as $chunk)
<div class="col">
#foreach($chunk as $tag)
<p><label>{!! $tag->name !!}</label><input type="text" name="tag_array[{!! $tag->id !!}]" value="{!! $tag->display_order !!}" class="numeric" />
#endforeach
</div>
#endforeach
<p>{!! Form::submit('Submit') !!}
{!! Form::close() !!}
When the form is submitted I then have the following function in my controller:
public function updateTagsOrder(TagsMenuRequest $request)
{
$i = 1;
foreach($request->tag_array as $tag_value)
{
$tag = new Tag;
$tag = Tag::find($i);
$tag->display_order = $tag_value;
$tag->save();
$i++;
}
flash()->overlay('The order of tags in the menu has been udpated!', 'Congratulations');
return redirect('/');
}
But this doesn't work. How would I rewrite this so I assigned the correct value for display_order to each tag. I'd be very grateful for your help.
foreach($request->tag_array as $tag_id => $tag_value)
{
$tag = Tag::find($tag_id);
$tag->display_order = $tag_value;
$tag->save();
}

Is there a more efficient way for grabbing the count of a model within the view? Laravel

I have Images. Each image has comments, and each comment has likes. In my view I'm wanting to display the number of likes a comment has by doing something like this:
#foreach ($images as image)
<img href="$image->url">
<div class="comment-box">
#foreach ($image->comments as $comment)
<div class="comment">
<span class="comment-owner">{{ $comment->owner()->name }}</span> {{ $comment->body }}
<div class="likes">
{{ $comment->likes()->count() }} // ** HERE IS MY N+1 PROBLEM **
</div>
</div>
#endforeach
</div>
#endforeach
However now I'm fetching a query each time there is a comment. There could be hundreds of comments on a page, so id be getting hundreds of queries just to find the count() for likes.
I believe there is a way you can grab the count in a controller by doing something like
DB::raw('count(*) as like_count')
but I do not know how to implement this in my situation. Can I put this kind of thing in my model so that I can always call something like {{ $comment->likes()->like_count }}? How can I make $comment->likes->count() work without having the N+1 problem?
Here is some more code:
Media Model
class Media extends Eloquent {
public function comments()
{
return $this->hasMany('Comment');
}
}
Comment Model
class Comment extends Eloquent {
public function media()
{
return $this->belongsTo('Media');
}
public function likes()
{
return $this->hasMany('Like');
}
}
Like Model
class Like extends Eloquent {
public function comment()
{
return $this->belongsTo('Comment');
}
}
My controller
public function imageViewer($id)
{
$images = Media::with(['comments' => function($query) {
$query->with('likes');
}])->where('resource_id', $id)->simplePaginate(1);
return View::make('image-viewer', compact('images'));
}
Why don't you just use blade's count like this count($comment->likes) assuming that $comment->likes gets the list of likes for a comment
#foreach ($images as image)
<img href="$image->url">
<div class="comment-box">
#foreach ($image->comments as $comment)
<div class="comment">
<span class="comment-owner">{{ $comment->owner()->name }}</span> {{ $comment->body }}
<div class="likes">
{{ count($comment->likes) }}
</div>
</div>
#endforeach
</div>
#endforeach
Thanks to #lukasgeiter for pointing out a mistake in the comments

Resources