Bring the latest commented topic on top on forum index page - php

I am building a forum. I want to bring published topics on top whenever users leave a reply. For topics without replies, I want to order them by created_at column.
How do you do that?
Forum controller
public function index()
{
$categories = Category::all();
$topics = Topic::with(['comments' => function ($query) {
$query->orderBy('comments.created_at', 'desc');
}])->paginate(20);
}
Here is my topic table
Schema::create('topics', function (Blueprint $table) {
$table->increments('id');
$table->integer('category_id')->unsigned();
$table->integer('user_id')->unsigned();
$table->string('title');
$table->text('body');
$table->timestamps();
});
Here is my comment table
$table->increments('id');
$table->text('reply');
$table->integer('user_id')->unsigned();
$table->integer('topic_id')->unsigned();
$table->foreign('topic_id')->refrenced('id')->on('topics')->onDelete('cascade');
$table->timestamps();
Comments model
class Comment extends Model
{
protected $fillable = [
'reply',
'user_id',
'topic_id'
];
public function topic()
{
return $this->belongsTo('App\Topic');
}
public function user()
{
return $this->belongsTo('App\User');
}
}
Topic model
class topic extends Model
{
protected $fillable = [
'title',
'body',
'category_id'
];
public function category()
{
return $this->belongsTo('App\category');
}
public function user()
{
return $this->belongsTo('App\User');
}
public function comments()
{
return $this->hasMany('App\Comment');
}
}
still trying to figure this out. any help will be hugely appreciated!!

Try using eager load with constraint:
public function index()
{
$categories = Category::all();
$topics = Topic::with(['comments' => function ($query) {
$query->orderBy('created_at', 'desc');
}])->paginate(20);
return view('forums.index',compact('categories','topics'));
}

DB::table('topics')
->leftjoin('comments', function($join) {
$join->on('topics.id', '=', 'comments.topic_id');
})
->select(DB::raw('IF(comments.id IS NULL,0, 1) as topic_comment'))
->orderBy('topic_comment', 'DESC')
->orderBy('topics.created_at', 'DESC')
->get();
the topic_comment as 1 records you can show on top right and rest wherever you want

Related

How to search relationship in laravel?

I have two models which are related. I am trying to do a search in orders and only display the actual search results instead of ALL orders of the category and user in which the order was found.
public function up()
{
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('category_id')->constrained()->cascadeOnDelete();
$table->foreignId('report_id')->constrained()->cascadeOnDelete();
$table->string('time');
$table->string('date');
$table->integer('issue_number');
$table->boolean('status');
$table->text('description')->nullable();
$table->timestamps();
});
}
So, what I WANT to achieve is the following. What I WANT to be displayed is:
OrderController.php
public function index()
{
$keyword = request('search') ?? null;
$orders = Order::query()->whereIn('user_id', $user->id)->whereIn('category_id', $category->id)
->orWhere(function ($query) use ($keyword) {
$query->when($keyword, function ($query) use ($keyword) {
$query->where('first_name' , 'LIKE' , "%{$keyword}%");
});
})->latest()->paginate(25);
return view('Admin.orders.index', compact('orders'));
}
Order.php
public function user()
{
return $this->belongsTo(User::class);
}
public function report()
{
return $this->belongsTo(Report::class);
}
public function category()
{
return $this->belongsTo(Category::class);
}
If I got you right you want to apply filter to your related table. For that kind of operation you can use whereHas or whereRelation methods of eloquent.
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
})->get();
or
$posts = Post::whereRelation(
'comments', 'created_at', '>=', now()->subHour()
)->get();
comments is related column.
For more information check Querying Relationship Existence.

Laravel relationship return null

I have a store that is using a payment package
Now I want to show the items that were purchased, but I run into this problem
Controller
public function mycourse()
{
$courses = PurchasedCourse::where('user_id', Auth::id())
->with('course')
->get();
dd($courses);
return view('student.courses.mycourse', [
'courses' => $courses
]);
}
Model
public function course()
{
return $this->belongsTo(Course::class, 'id');
}
Migration
public function up()
{
Schema::create('courses', function (Blueprint $table) {
$table->id('id');
$table->string('name')->unique();
$table->string('start');
$table->string('end');
$table->integer('price');
$table->string('jalasat');
$table->string('zarfiat');
$table->text('tozih');
$table->integer('hit');
$table->string('department');
$table->string('thumbnail');
$table->tinyInteger('status')->default(0);
$table->string('slug')->unique();
$table->timestamps();
});
}
Your relationship method is wrong. The syntax for the belongsTo() method is
belongsTo(class, ?foreign_id, ?related_id). In your case, it should be:
public function course()
{
return $this->belongsTo(Course::class, 'course_id', 'id');
}
or just
public function course()
{
return $this->belongsTo(Course::class);
}
since your columns follow Laravel's naming conventions.

How can I best optmize these queries in Laravel?

I have a simple script in post.blade.php
{{ Auth::user() ? ($post->ratings()->where('user_id', Auth::user()->id)->exists() ? 'You rated ' . $post->userRating($post) : '') : '' }}
The 2 functions that get access in my post model are:
public function ratings() {
return $this->hasMany(Rating::class);
}
public function userRating(Post $post) {
return $this->ratings()->where([
['post_id', '=', $post->id],
['user_id', '=', Auth::user()->id]
])->first('stars')->stars / 2;
}
This is the index in my postcontroller:
public function index() {
$posts = Post::with('user')->withAvg('ratings', 'stars')->paginate(100);
return view('posts.index', [
'posts' => $posts,
]);
}
This however takes about 1 second to load each page because it has to execute that code 100 times.
If I remove the script mentioned at the top from the blade file the page loads way faster.
I made this query in raw MySQL and it loads the results significantly faster:
select `posts`.*, (select avg(`ratings`.`stars`) from `ratings` where `posts`.`id` = `ratings`.`post_id`) as `ratings_avg_stars`, (SELECT count(*) FROM ratings WHERE post_id = posts.id and user_id = 1) as rated from `posts` where `posts`.`deleted_at` is null
If I were to put that in my postcontroller I think the page would load faster, I don't know how to convert the MySQL to Eloquent, I have tried a query converter but those get stuck on the subqueries.
How can I convert the MySQL query to an Eloquent query?
I believe you are looking to Constrain Your Eager Loading and create an access mutator for Post.
You can see my fiddle here.
What this approach does, is load all the necessary data in one go, using the constrained eager load, and the access mutator will have this relationship loaded already when it appends the ratingAverage to the post.
This way you can avoid any unnecessary database calls, or method calls to recompute values you already have.
Schema:
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->string('title');
$table->string('body');
$table->timestamps();
});
Schema::create('ratings', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->unsignedInteger('post_id');
$table->integer('stars');
$table->timestamps();
});
Models:
use \Illuminate\Database\Eloquent\Relations\Pivot;
class User extends Model
{
protected $guarded = [];
}
class Rating extends Pivot
{
protected $table = 'ratings';
protected $guarded = [];
public static function rate(User $user, Post $post, int $stars)
{
return static::create([
'user_id' => $user->id,
'post_id' => $post->id,
'stars' => $stars
]);
}
public function user()
{
return $this->belongsTo(User::class);
}
public function post()
{
return $this->belongsTo(Post::class);
}
}
class Post extends Model
{
protected $guarded = [];
public function user()
{
return $this->belongsTo(User::class);
}
public function ratings()
{
return $this->hasMany(Rating::class);
}
public function getRatingAverageAttribute()
{
return $this->ratings->sum('stars') / $this->ratings->count();
}
}
Controller:
public function index() {
$posts = Post::query()->with(['ratings' => function ($query) {
$query->where('user_id', Auth::id());
}])->get();
return view('posts.index', [
'posts' => $posts,
]);
}
And finally in blade:
#if($post->ratings->count())
{{ 'You rated ' . $post->ratingAverage }}
#endif

Laravel 5.8: MorphMany relation returns Empty

Comment Model:
public function commentable()
{
return $this->morphTo();
}
public function comments()
{
return $this->hasMany(Comment::class , 'parent_id' , 'id');
}
public function setCommentAttribute($value)
{
$this->attributes['comment'] = str_replace(PHP_EOL , "<br>" , $value);
}
Post Model:
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
And Controller:
public function show_comments(Post $post)
{
$comments = $post->comments()
->where('approved' , 1)
->where('parent_id', 0)
->latest()
->with(['comments' => function($query) {
$query->where('approved' , 1)->latest();
}])->get();
dd($comments);
return view('post',compact('comments'));
}
Database table Comments:
Schema::create('comments', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('parent_id')->unsigned()->default(0);
$table->boolean('approved')->default(0);
$table->text('comment');
$table->integer('commentable_id')->unsigned();
$table->string('commentable_type');
$table->timestamps();
});
$dd($comments) returns #items: [] or Empty. There are database records and I can access them with another methods.
I did searching alot before asking but no luck.
I was trying to resolve the same issue for a few hours. For anyone searching for the answer :
Check if the commentable_type field in comments table has properly formatted route strings
'commentable_type' => 'App/Models/Comment', // Does not work
'commentable_type' => 'App\Models\Comment', // Works 🥳

eloquent with multiples tables data and relationship

I have these 3 tables:
Schema::create('companies', function (Blueprint $table) {
$table->increments('id');
$table->integer('city_id')->unsigned();
$table->string('name');
$table->string('address');
$table->float('lat', 10,6);
$table->float('lng', 10,6);
$table->timestamps();
$table->foreign('city_id')->references('id')->on('cities');
});
Schema::create('company_clients', function (Blueprint $table) {
$table->increments('id');
$table->integer('company_id')->unsigned();
$table->integer('client_id')->unsigned();
$table->foreign('company_id')->references('id')->on('companies');
$table->foreign('client_id')->references('id')->on('companies');
});
Schema::create('cities', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
});
Now, I want to have an eloquent query, where it will return an array (Not only one item) of companies, where the (for example)company_id=1 on company_clients table. Also, the city_id is suppose to return the name and not the id, using the cities table. I cannot imagine how to do it right now.
I made:
class City extends Model
{
protected $table = 'cities';
public $timestamps = false;
protected $fillable = [
'name',
];
public function companies()
{
return $this->hasMany('App\Company', 'city_id', 'id');
}
}
class CompanyClients extends Model
{
protected $table = 'company_clients';
public $timestamps = false;
protected $fillable = [
'company_id', 'client_id',
];
public function companies()
{
return $this->belongsTo('App\Company', 'company_id', 'id');
}
public function clients()
{
return $this->belongsTo('App\Company', 'company_id', 'id');
}
}
class Company extends Model
{
protected $table = 'companies';
protected $fillable = [
'name', 'address', 'lat', 'lng', 'city_id',
];
protected $hidden = [
'clients', 'created_at', 'updated_at',
];
public function city()
{
return $this->belongsTo('App\City', 'city_id', 'id');
}
public function companies()
{
return $this->hasMany('App\CompanyClients', 'company_id', 'id');
}
public function clients()
{
return $this->hasMany('App\CompanyClients', 'client_id', 'id');
}
}
But, I'm missing the code in the controller. I tried:
$result = Company::leftJoin('company_clients', function($join) {
$join->on('companies.id', '=', 'company_clients.company_id');
})->where('company_clients.company_id', '=', 1 )->get();
or
$result = Company::with(['clients' => function($q){
$q->where('company_id', 1);
}])->get();
but is not returning the correct result. What I'm missing?
Thanks!
EDITED:
I had found a way, but I'm not sure if is the best way to do it. Can someone please confirm?
$result = Company::join('company_clients', function($join) {
$user = Auth::guard('api')->user();
$join->on('companies.id', '=', 'company_clients.client_id')->where('company_clients.company_id', '=', $user->company_id );
})->join('cities', 'cities.id', '=', 'companies.city_id')->get(array('companies.*', 'cities.name'));
Try
CompanyClients::with('company.city', 'clients')->where('company_id', 1)->get();
Rename
companies
relationship on the CompanyClients model to
company

Categories