Thread not returning all subscribed users - mysql

About
I am using Laravel 5.8 with MySQL. I have a thread table and thread details table. I am fetching all records from threads and it's associated from threaddetails table wherever there are matching user_id found in threaddetails.
Expected
It should return all my thread and subscribed users present in the thread.
Current
It returns all my threads but from threaddetails table it returns my records only. It does not returns other subscribed users with which I am chatting.
Question:
Am I missing anything in Query to fetch the data section?
Table: Thread - Schema
Schema::create('tblthread', function (Blueprint $table) {
$table->unsignedMediumInteger('thread_id')->autoIncrement();
$table->timestamp('created_on');
});
Table Thread Sample Data
INSERT INTO tblthread (thread_id, created_on) VALUES
(1, '2019-08-07 20:30:54');
Table Thread Details
Schema::create('tblthreaddetails', function (Blueprint $table) {
$table->unsignedMediumInteger('thread_detail_id')->autoIncrement();
$table->unsignedMediumInteger('thread_id');
$table->unsignedMediumInteger('user_id')->nullable();
$table->foreign('thread_id')->references('thread_id')->on('tblthread')->onDelete('cascade');
});
Sample Data - Thread Details
INSERT INTO `tblthreaddetails` (`thread_detail_id`, `thread_id`, `user_id`) VALUES
(1, 1, 1),
(2, 1, 6);
Query to fetch the data
ThreadModel::with(["Details" => function($query) use ($user_id) {
$query->where("user_id", $user_id);
}])->get();
Thread Model
class ThreadModel extends Model
{
public $table = 'tblthread';
public $primaryKey = 'thread_id';
public $timestamps = false;
public function Details() {
return $this->hasMany("\ThreadDetailsModel", "thread_id", "thread_id");
}
}
Thread Details Model
class ThreadDetailsModel extends Model
{
public $table = 'tblthreaddetails';
public $primaryKey = 'thread_detail_id';
public $timestamps = false;
}

tblthread should be associated to a user. Hence you can define user_id column on it.
The query should look like this:
ThreadModel::where('user_id', $user_id)->with("Details")->get();
// So first you get all the threads that belong to you
// and then get all details for those threads (only)
Hope it helps!

The problem is here:
ThreadModel::with(["Details" => function($query) use ($user_id) {
$query->where("user_id", $user_id);
}])->get();
This query will filter all the related details that only contain the user_id = $user_id. So, of course, only the details related to the $user_id will be returned. So, basically you are constraining the related models.. not the threads itself. So with this, there might be some Thread returned that doens't have any detail associated with the user $user_id which I assume you don't want...
Try this instead:
Check if a Thread has at least one associated Detail that belongs to the user_id = $user_id.
Load all the Details of those Threads.
So this should work:
use Illuminate\Database\Eloquent\Builder;
// ...
public function myCoolMethod()
{
$threads = ThreadModel
::has('details', function (Builder $query) use ($user_id) {
$query->where('user_id', $user_id);
})
->with('details')
->get();
// ...
}
- With the has() method we are checking relationship existence.
- With the with() method we are eager loading all the related details of those selected threads.

whereHas solved my issue
ThreadModel
::whereHas('details', function (Builder $query) use ($user_id) {
$query->where('user_id', $user_id);
})
->with('details')
->get();

Related

Eloquent query doesn't select a specific ID

This question is similar to this question
I haven't changed the default autoIncrement from the table migration, however I noticed i couldn't get a model from the ID for a specific row. For the other rows it works fine. I cleared cache thinking it was a cache issue, but seems it is not.
Behaviour
I got records 1,..., 58, 59, 60
When i select a model
$object = Post::find(59);
// $object returns null but record exists
However i added another record via the app to check if the behaviour is the same, and the record from 60 is not null and it is the expected behaviour. Has anyone encountered this? If so what would be the best approach to overcome this.
I am using XAMPP v8.0.8 on Windows
Edit:
Post Model
class Post extends Model
{
use HasFactory,Searchable,SoftDeletes;
protected $fillable = [
'hash_id','user_id','location','subjects','request_type'
];
protected $casts = [
'location' => 'array',
'subjects' => 'array'
];
public function User()
{
return $this->belongsTo('App\Models\User');
}
public function searchableAs()
{
return 'posts';
}
}
Migration file
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('hash_id')->unique();
$table->integer('user_id');
$table->json('location');
$table->json('subjects');
$table->string('request_type');
$table->timestamps();
$table->softDeletes();
});
}
Assuming from soft deletes this happens if the record is deleted. Try looking in the deleted_at field on database.

Why does Laravel redirect to a wrong id after DB::listen?

I would like to store every query I run inside my application. To do that, I've created a "loggers" table, a Logger modal and this function inside my boot AppServiceProvider
DB::listen(function($query) {
$logger = new Logger;
$logger->query = str_replace('"', '', $query->sql);
$logger->bindings = json_encode($query->bindings);
$logger->created_at = Carbon::now();
$logger->save();
});
Well, anytime I run an insert query, Laravel returns the loggers table ID instead of the model last inserted ID.
Why on earth does this happen?
public function store(CycleRequest $request) {
$appointment = new Appointment;
$appointment-> ... same data ...
$appointment->save();
if ( ! $errors ) ){
$msg['redirect'] = route('appointments.edit', $appointment);
// The page redirects to last logger id
}
}
I think you want to do triggers so I recommend use events for this, o maybe you can take the structure of you table "loggers" and do two differents querys each time that you do querys.
After searching a lot, I found a solution creating a Middleware. Hope this may help someone else.
I've created a QueryLogMiddleware and registered in 'web' middleware, but you may put it everywhere you want:
public function handle($request, Closure $next)
{
\DB::enableQueryLog();
return $next($request);
}
public function terminate($request, $response)
{
$queries = \DB::getQueryLog();
foreach ($queries as $query)
{
// Save $query to the database
// $query["query"]
// $query["bindings"]
...
}
\DB::disableQueryLog();
\DB::flushQueryLog();
}

One to Many relationship, sort and filter children

I want to display all posts which like the user. OK. I can use this:
$user = User::where('slug', $user->slug)
->first();
dd($user->likes);
But it doesn't what I want to do. Which any post have to be accept by moderator (->where('accept', 1)) and orderign (->orderBy('created_at', 'desc')).
Who know how I can do that?
Currently I have 2 models. My relationships:
//Post
public function likes(){
return $this->hasMany('App\Like');
}
//Likes
public function post(){
return $this->belongsTo('App\Post');
}
//migrate of likes look like this
Schema::create('likes', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('user_id');
$table->integer('post_id');
$table->timestamps();
});
How I can solve my problem?
You could set up an M:N relationship between User and Post using Likes as pivot.
# User model
public function likes()
{
return $this->hasMany(Likes::class, 'user_id');
}
public function liked_posts()
{
return $this->belongsToMany(Post::class, 'likes', 'user_id', 'post_id');
}
# Likes model
public function post()
{
return $this->belongsTo(Post::class);
}
You could set it up like you have, User has many Likes belongs to Post.
// Using only likes and Likes's post relationship
$user = User::with(['likes.post' => function ($posts) {
$posts->where('accept', 1)->orderBy('created_at', 'desc');
}])->where('slug', $slug)->first();
Or you could use the M:N relationship.
// Using the liked_posts relationship
$user = User::with(['liked_posts' => function ($posts) {
$posts->where('accept', 1)->orderBy('created_at', 'desc');
}])->where('slug', $slug)->first();

How to improve performance of multiple count queries in a laravel view

I'm working on a marketing application that allows users to message their contacts. When a message is sent, a new "processed_message" database entry is created. There is a list view that displays all campaigns and the number of messages sent, blocked and failed for each campaign. My problem is that this list view takes way too long to load after there are > 50 campaigns with lots of messages.
Currently each campaign has 3 computed attributes (messages_sent, messages_failed and messages_blocked) that are all in the Campaign model's "appends" array. Each attribute queries the count of processed_messages of the given type for the given campaign.
namespace App;
class Campaign
{
protected $appends = [
'messages_sent',
'messages_blocked',
'messages_failed'
];
/**
* #relationship
*/
public function processed_messages()
{
return $this->hasMany(ProcessedMessage::class);
}
public function getMessagesSentAttribute()
{
return $this->processed_messages()->where('status', 'sent')->count();
}
public function getMessagesFailedAttribute()
{
return $this->processed_messages()->where('status', 'failed')->count();
}
public function getMessagesBlockedAttribute()
{
return $this->processed_messages()->where('status', 'blocked')->count();
}
}
I also tried to query all of the messages at once or in chunks to reduce the number of queries but getting all of the processed_messages for a campaing at once will overflow memory and the chunking method is way too slow since it has to use offset. I considered using eager loading the campaigns with processed_messages but that would obviously use way too much memory as well.
namespace App\Http\Controllers;
class CampaignController extends Controller
{
public function index()
{
$start = now();
$campaigns = Campaign::where('user_id', Auth::user()->id)->orderBy('updated_at', 'desc')->get();
$ids = $campaigns->map(function($camp) {
return $camp->id;
});
$statistics = ProcessedMessage::whereIn('campaign_id', $ids)->select(['campaign_id', 'status'])->get();
foreach($statistics->groupBy('campaign_id') as $group) {
foreach($group->groupBy('status') as $messages) {
$status = $messages->first()->status;
$attr = "messages_$status";
$campaign = $campaigns->firstWhere('id', $messages->first()->campaign_id);
$campaign->getStatistics()->$attr = $status;
}
}
return view('campaign.index', [
'campaigns' => $campaigns
]);
}
}
My main goal is to reduce the current page load time considerably (which can take anywhere from 30 seconds to 5 minutes when there are a bunch of campaigns).
You could use the withCount method to count all the objects without loading the relation.
Reference:
If you want to count the number of results from a relationship without actually loading them you may use the withCount method, which will place a {relation}_count column on your resulting models.
In your controller you could do this:
$count = Campaign::withCount(['processed_messages' => function ($query) {
$query->where('content', 'sent');
}])->get();
You could do multiple counts in the same relationship too:
$campaigns = Campaign::withCount([
'processed_messages',
'processed_messages as sent_message_count' => function ($query) {
$query->where('content', 'sent');
}],
'processed_messages as failed_message_count' => function ($query) {
$query->where('status', 'failed');
}],
'processed_messages as blocked_message_count' => function ($query) {
$query->where('status', 'blocked');
}])->get();
You can access the count with this:
echo $campaigns[0]->sent_message_count
Docs

Laravel 5.4 connect two tables

First table (leads)
id, website, name, user_id
Second table (flags)
id, lead_id, user_id, info
What I am trying to do
Get all the user flags and each flag lead information.
what I have tried
Flag model
public function main()
{
return $this->belongsTo('App\Main', 'lead_id');
}
Flag Controller
public function getAgentFlags()
{
$agent_id = Auth::user()->id;
$flags = Flag::whereHas('main', function ($q) {
$q->where('user_id', '=', Auth::user()->id);
})->get();
dd($flags);
$totalleads = Flag::where('user_id', '=', $agent_id)->count();
return view('flags.my-flags')
->withLeads($leads)
->withTotalleads($totalleads);
}
What it returns
It returned wrong information as it return leads which is not equal to user ID.