Reputation: 267
I'm currently putting together some code that allows for a user to search an activities table in multiple ways (i.e. if title checkbox is selected) I feel like my code looks a little messy so I wanted to come on stack overflow and ask everyone what would be the best way to make this code more elegant? I'm just looking for ways to improve, have more readable code, and have a better structure for it.
if (request('name')){
$name = request('name');
$user = User::where('name', $name)->firstOrFail();
if (request('title') == 1) {
$activities = Activity::with('activity')->where('user_id', $user->id)->whereHas('thread', function ($query) use ($search, $user) {
$query->where('threads.user_id', '=', $user->id)
->where('threads.title', 'LIKE', '%' . $search . '%');
})->get();
dd($activities);
} else {
$activities = Activity::with('activity')->where('user_id', $user->id)->whereHas('thread', function ($query) use ($search, $user) {
$query->where('threads.user_id', '=', $user->id)
->where('threads.title', 'LIKE', '%' . $search . '%')
->orWhere('threads.body', 'LIKE', '%' . $search . '%');
})->orWhereHas('reply', function ($query) use ($search, $user) {
$query->where('replies.user_id', '=', $user->id)
->where('replies.body', 'LIKE', '%' . $search . '%');
})->get();
dd($activities);
}
} else {
if (request('title') == 1) {
$activities = Activity::with('activity')->where('user_id', $user->id)->whereHas('thread', function ($query) use ($search, $user) {
$query->where('threads.title', 'LIKE', '%' . $search . '%');
})->get();
dd($activities);
} else {
$activities = Activity::with('activity')->whereHas('thread', function ($query) use ($search) {
$query->where('threads.body', 'LIKE', '%' . $search . '%')
->orWhere('threads.title', 'LIKE', '%' . $search . '%');
})->orWhereHas('reply', function ($query) use ($search) {
$query->where('replies.body', 'LIKE', '%' . $search . '%');
})->get();
}
}
Thank you!
Upvotes: 0
Views: 93
Reputation: 15786
You could use the query builder's when
and unless
methods as well as define some query scopes in your models so your end result could look something like this
$user = request('name') ? User::where('name', $name)->firstOrFail() : null;
$title = request('title') == 1;
$activities = Activity::with('activity')->search($search, $user, $title)->get();
# Activity model
public function scopeSearch(Builder $query, ?$search = null, ?User $user = null, bool $title = false)
{
if (!$search)
return $query;
else
return $query->when($user, fn($q) => $q->where('user_id', $user->id))
->whereHas('thread', fn($thread) => $thread->search($search, $user))
->unless($title, fn($q) => $q->orWhereHas('reply', fn($reply) => $reply->search($search, $user)));
}
# Thread model
public function scopeSearch(Builder $query, ?string $search = null, ?User $user = null)
{
if (!$search)
return $query;
else
return $query->when($user, fn($q) => $q->where('threads.user_id', $user->id))
->where(fn($q) => $q->where('threads.title', 'LIKE', "%$search%")
->orWhere('threads.body', 'LIKE', "%$search%"));
}
# Reply model
public function scopeSearch(Builder $query, ?string $search = null, ?User $user = null)
{
if (!$search)
return $query;
else
return $query->when($user, fn($q) => $q->where('replies.user_id', $user->id))
->where('replies.body', 'LIKE', "%$search%");
}
Scopes are basically reusable queries. You can define them at a global level (for all models) or at a local level (this is what I've done here).
With them, I've moved nearly all the query related logic to the models but if you prefer, you could still write it all in the controller.
Using the scopes I defined,
$activities = Activity::with('activity')
// call Activity Model's search scope
->search($search, $user, $title)
->get();
translates to
$activities = Activity::with('activity')
->when($user, fn($q) => $q->where('user_id', $user->id))
// call Thread model's search scope in whereHas('thread', ...) closure
->whereHas('thread', fn($thread) => $thread->search($search, $user))
// call Reply model's search scope in whereHas('reply', ...) closure
->unless($title, fn($q) => $q->orWhereHas('reply', fn($reply) => $reply->search($search, $user)))
->get();
which in turn translates to
$activities = Activity::with('activity')
->when($user, fn($q) => $q->where('user_id', $user->id))
->whereHas('thread', function ($thread) use ($search, $user) {
$thread->when($user, fn($q) => $q->where('threads.user_id', $user->id))
->where(fn($q) => $q->where('threads.title', 'LIKE', "%$search%")
->orWhere('threads.body', 'LIKE', "%$search%"));
})
->unless($title, fn($q) => $q->orWhereHas('reply', function ($reply) use ($search, $user) {
$reply->when($user, fn($q) => $q->where('replies.user_id', $user->id))
->where('replies.body', 'LIKE', "%$search%");
}))
->get();
The $title
variable could be inlined at this point. ->unless(request('title') == 1, ...)
when()
, unless()
.where(fn($q) => ...)
.Upvotes: 1