Retrieving correct data from the database using Laravel Eloquent - laravel

I am trying to retrieve data from the database however I am not getting right results. It looks like my "If" statements are not working properly as I am getting some data from the "$sale->whereRaw" query which is at the beginning of foreach. What I am doing wrong?
I am working with Laravel 4.2.
Controller:
$matches = Match::where('PeopleID', '=', $id);
$sales = Sale::leftJoin('property', 'sales.property_id', '=', 'property.property_id')
->where('Status', '=', 'ForSale');
foreach($matches as $match)
{
$county = $match->county;
$sales->whereRaw("match (`county`) against (?)", array($county));
if($match->tenureFh == '1')
{
$sales->where('Tenure','=','FH');
if($match->minPrice != '0')
{
$sales->where('PriceFreehold', '>=' , $match->minPrice);
}
if($match->maxPrice != '0')
{
$sales->where('PriceFreehold', '<=', $match->maxPrice);
}
}
if($match->tenureLh == '1')
{
$sales->where('Tenure', '=', 'LH');
if($match->minPrice != '0')
{
$sales->where('PriceLeasehold', '>=' , $match->minPrice);
}
if($match->maxPrice != '0')
{
$sales->where('PriceLeasehold', '<=', $match->maxPrice);
}
}
}
$results = $sales->orderBy('sales.updated_at', 'asc')->get();
Match Model
public function people()
{
return $this->belongsTo('People', 'PeopleID', 'PeopleID');
}
public function sale()
{
return $this->hasMany('Sale');
}
Sale Model
public function match()
{
return $this->belongsTo('Match');
}
People Model
public function matches()
{
return $this->hasMany('Match', 'PeopleID', 'PeopleID');
}
public function sale()
{
return $this->hasMany('Sale');
}
#uptade
Currently I am getting the results from this statement:
$sales->whereRaw("match (`county`) against (?)", array($county));
Which is good however I need to filter the results further and this is where the "if" statements come in to place.
"Match" contains different searching credentials for each customer. If the candidate is looking for product with "Tenure: FH" In county "A" I need to display products with county "A" and which "Tenure" is "FH" (That is why I have made some those "if" statements). However those statements does not seem to work as I am getting ALL results for county "A" which "Tenure" is "FH" or other than "FH".
So the problem must be with those "Ifs" as they are not filtering the result further.
Imagine you have signed in with cars dealership website and you are looking for a red, 4 door car however the result you are getting is ALL 4 door cars but with all different colours (but you wanted a red one right?) - This is how it works at the moment.

I think you can do dd(DB::getQueryLog()); to see what query you get in the end.

Related

Eloquent query : Retrieve the list of offices where the user possess all the desks, not just one (nested whereHas)

I want to retrieve all the offices ( with the desks eager loaded) but I only want offices where the user possess all the desks in the office
I have the following models and relationships between them :
I came up with the following query which seems to almost work :
<?php
Office::query()
->whereHas('desks', function ($query) {
$query->whereHas('possessedDesks', function ($query) {
$query->where('user_id', auth()->id);
});
})
->with(['desks'])
->get();
The current query seems to return a result where if a user own a single desk in the office then the office is returned in the query. Am I missing something ? Is there a way to be more strict in the whereHas to have some kind of and instead of a or
Thanks in advance for your help ;)
Edit :
Thanks to Tim Lewis's comment I tried this with not more result :
<?php
Office::query()
->withCount('desks')
->whereHas('desks', function ($query) {
$query
->whereHas('possessedDesks', function ($query) {
$query->where('user_id', auth()->id);
})
->has('possessedDesks', '=', 'desks_count');
})
->with(['desks'])
->get();
Edit 2 :
I managed to get exactly what I need, outside of an Eloquent query. The problem is still persist since I need it to be in an Eloquent query because I need this for a query string request (Search engine).
<?php
$offices = Office::query()
->with(['desks'])
->get();
$possessedDeskIds = auth()->user->with('possessedDesks.desk')->possessedDesks()->get()->pluck('desk.id');
$fullyOwnedOffices = [];
foreach($offices as $office) {
$officeDeskIds = $office->desks()->pluck('id');
$atLeastOneDeskIsNotPossessed = false;
foreach($officeDeskIds as $officeDesk) {
if ($possessedDeskIds->doesntContain($officeDesk)) {
$atLeastOneAromaIsNotPossessed = true;
break;
}
}
if (!$atLeastOneDeskIsNotPossessed) {
$fullyOwnedOffices[] = $office;
}
}
Edit 3 :
Ok, With the previous edit and the need to have some kind of one line query (for the query string of a search engine) I simplified the request since the nested whereHas where hard to make sense of.
It's not the prettiest way to do it, It add more query for the process, but with the code from the Edit2 I can generate an array of Ids of the Office where all the Desk are possessed by the user. With that I can just say that when this option is required in the search engine, I just select the ones my algorithm above gave me and no more logic in the query.
If some genius manage to find a way to optimize this query to add the logic back inside of it, I'll take it but for now it works as expected.
Thanks Tim for your help
<?php
class SearchEngineController extends Controller
{
public function index(Request $request) {
$officesWithAllDesksPossessed = collect([]);
if ($request->has('with_possessed_desks') && $request->input('with_possessed_desks')) {
$publicOffices = Office::query()
->isPublic()
->with(['desks'])
->get();
$possessedDeskIds = currentUser()
->possessedDesks()
->with('desk')
->get()
->pluck('desk.id');
foreach($publicOffices as $office) {
$publicOfficesDeskIds = $office->desks()->pluck('id');
$atLeastOneDeskIsNotPossessed = false;
foreach($publicOfficesDeskIds as $officeDesk) {
if ($possessedDeskIds->doesntContain($officeDesk)) {
$atLeastOneDeskIsNotPossessed = true;
break;
}
}
if (!$atLeastOneDeskIsNotPossessed) {
$officesWithAllDesksPossessed->push($office);
}
}
$officesWithAllDesksPossessed = $officesWithAllDesksPossessed->pluck('id');
}
return Inertia::render('Discover', [
'offices'=> OfficeResource::collection(
Office::query()
->isPublic()
->with(['desks'])
->when($request->input('search'), function ($query, $search) {
$query->where('name', 'like', "%{$search}%");
})
->when($request->input('with_possessed_desks'), function ($query, $active) use($officesWithAllDesksPossessed) {
if ($active === 'true') {
$query->whereIn('id', $officesWithAllDesksPossessed);
}
})
->paginate(10)
->withQueryString()
),
'filters' => $request->only(['search', 'with_possessed_desks']),
]);
}
}

Using WhereRaw on collection ignores relation

I made a route to search a particular collection - Customers.
Customer Model
public function location() {
return $this->belongsTo('App\Location');
}
Location Model
public function customers() {
return $this->hasMany('App\Customer');
}
On the index page, I'm simply showing the customers with the data from $location->customers()
$location comes from the model route binding.
This is my search controller:
if ($request->input('search') != null) {
$customers = $location->customers();
$search = strtoupper($request->input('search'));
$searchQuery = 'UPPER(email) LIKE (?) OR CONCAT(UPPER(first_name), " ", UPPER(last_name)) LIKE (?)';
$customers = $location->customers()->whereRaw($searchQuery, ["%{$search}%", "%{$search}%"]);
$customers = $customers->paginate();
}
return response()->json(['results' => $customers], 200);
When a search is executed, I get 10 times as many results in some cases because it's grabbing all customers rather than the specific location relationship.
How can I make whereRaw use the relation?
It is because the OR condition messes up with the query. To avoid that wrap those two query parts with brackets ().
$searchQuery = '( UPPER(email) LIKE (?) OR CONCAT(UPPER(first_name), " ", UPPER(last_name)) LIKE (?) )';
Eloquent builder approach:
$customers = $location->customers()->where(function($query) use ($search) {
$query->whereRaw('UPPER(email) LIKE (?)', ["%{$search}%"]);
$query->orWhereRaw('CONCAT(UPPER(first_name), " ", UPPER(last_name)) LIKE (?)', ["%{$search}%"]);
});
Explanation
Example logical expression:
firstCondition && secondCondition || thirdCondition
In above example expression thirdCondition does not even care about firstCondition or secondCondition because of the ||.
So if you want to check this thirdCondition with secondCondition, then you have to explicitly wrap this two conditions with brackets ().
firstCondition && ( secondCondition || thirdCondition )
This same logic applies for mysql queries too.

Laravel, Eloquent, getting related items from a morph relationship (advice)

The title of the question may seem weird, but I'll explain it briefly. I'm not experiencing any issues yet, but considering any advice regarding the following approach.
I have the following tables:
articles
hasMany(tags)
movies
hasMany(tags)
series
hasMany(tags)
subs (morph)
media_id
media_type
user_id
All of the first three tables have many tags, where the subs table have a Morph relation between them all.
The concept is simple, the user can subscribe to movies and/or series and NOT articles, let's say, opt-in a record from movies.
The following record will be inserting:
media_id => 1, media_type => movie, user_id => 1
Let's say we have 10K records like this in subs table. Now, I create a record in articles with specific tags. I want to notify all users in subs, who are opted-in movies and/or series that have exactly the same tags as the record I'm targeting from articles.
TL;DR: Notifying users that are interested in specific tags from movies or series.
To achieve this, I added the morphTo in the subs model:
public function media(){
return $this->morphTo();
}
The logic is straightforward:
Get the article.
Get the tags() relationship and save it into a variable.
Check if tags are not empty, if not, continue
Get all the subs.
Get the morphTo relationship and get its tags.
Compare and continue.
$article = MediaArticle::find($notificationable_id);
$article_tags = $article->tags;
$subbed_users = array();
if (empty($article_tags) === false) {
$subs = NotificationMediaSub::all();
foreach ($subs as $sub) {
$sub_tags = $sub->media->tags;
foreach ($sub_tags as $sub_tag) {
foreach ($article_tags as $article_tag_title) {
if (strcasecmp($sub_tag->title, $article_tag_title->title) === 0) {
array_push($subbed_users, $sub->user_id);
}
}
}
}
// continue here
}
I find this approach really intensive and may cause the server to slow down a lot. Is there a better approach to achieve my goal?
=========================
Approach
I defined the media_tags_relationship as a Model, inside it:
public function media() {
if ($this->taggable_type === AdminHelper::MORPH_MEDIA_TRANSLATION_MOVIE['original']) {
return $this->belongsTo(MediaMovie::class, 'taggable_id', 'id');
} elseif ($this->taggable_type === AdminHelper::MORPH_MEDIA_TRANSLATION_SERIES['original']) {
return $this->belongsTo(MediaSeries::class, 'taggable_id', 'id');
}
return $this->belongsTo(MediaMovie::class, 'taggable_id', 'id')->where('id', 0);
}
Now, I'm fetching the subs like this:
$article = MediaArticle::find($notificationable_id);
$article_tags = $article->tags;
$subbed_users_tokens = array();
if (empty($article_tags) === false) {
$article_tags = $article_tags->map(function ($tag) {
return $tag->title;
});
$related_tags = MediaTag::whereIn("title", $article_tags)->get();
$related_tags = $related_tags->map(function ($tag) {
return $tag->id;
});
$tags_relationship = MediaTagsRelationship::whereIn("tag_id", $related_tags)->where("taggable_type", "movie")->orWhere("taggable_type", "series")->get();
foreach ($tags_relationship as $tag_relationship) {
$media = $tag_relationship->media()->with('subs')->get();
if (empty($media) === false) {
$media = $media[0];
$subs = $media->subs->map(function ($sub) {
return $sub->firebase_token;
});
array_push($subbed_users_tokens, $subs);
}
}
}

laravel 5.2 if else to query database

stuck on a form that allows the user to enter a value into a choice of two fields. I can query the database using one field but want to add more range to database queries. With the following code below when i try to access the page to query it just shows me a white screen.
public function index()
{
$data = $request->all();
if(!empty($data['pstoreNum']))
{
$pstoreNum = $data['pstoreNum'];
$result = DB::table('perfumes')->where('StoreNumber','=',$pstoreNum)
->get();
return view('perfumes',compact('result'));
}
else if(!empty($data['pweekNum']))
{
$pweekNum = $data['pweekNum'];
$result = DB::table('perfumes')->where('WeekNumber','=',$pweekNum)
->get();
return view('perfumes',compact('result'));
}
}
My routes file simple calls the index function. Any help would be appreciated.
You can add query functions within your query like so
public function index(Request $request)
{
$data = $request->all();
$result = \DB::table('perfumes')->where(function($query) use ($data) {
if(!empty($data['pstoreNum'])) {
$query->where('StoreNumber', '=', $data['pstoreNum']);
}
if(!empty($data['pweekNum'])) {
$query->where('WeekNumber', '=', $data['pweekNum']);
}
})->get();
return view('perfumes',compact('result'));
}
You can then use the one query and add multiple wheres on various conditions.
https://laravel.com/docs/5.2/queries#advanced-where-clauses

getting Locale session on Laravel and selecting query accordingly

I am trying to just select a sql query depending on the session set for the locale language. Yet, as simple as it should be, now I am getting at the View an "invalid argument for the foreach array", that is, it is not passing arguments. Before having the check locale addition, it did return correctly for English, but as I am adding now another language, the IF is not taking decisions because the check session somehow is not properly written or I dont know what. I have been googling and it read like this:
if (\Session::get('locale') == 'en')
{
$listings = \DB::table('properties')
->join('english_descriptions', 'property_id', '=', 'properties.id' )
->select('endescription','properties.id','houses1','price', 'bedrooms', 'm2', 'entitle')
->get();
return $listings;
}
else{
if (\Session::get('locale') == 'es')
{
$listings = \DB::table('properties')
->join('spanish_descriptions', 'property_id', '=', 'properties.id' )
->select('esdescription','properties.id','houses1','price', 'bedrooms', 'm2', 'estitle')
->get();
return $listings;
}
My controller function looks like this now:
public function showListings()
{
var_dump(\Session::getLocale());
$listings = Property::returnListings();
return \View::make('listings', compact('listings'));
}
Make sure you always have a fallback return value when using if-statements.
$listings = []; // this will be the default return value if no condition is met
if (\Session::get('locale') == 'en') {
// query for listings in 'en'
}
else if (\Session::get('locale') == 'es') {
// query for listings in 'es'
}
return $listings;
Now for the locale Session, dump the value of \Session::get('locale') to check if the value is really en or es.
You might want to retrieve your locale value not from session but from the App itself using \App::getLocale();

Resources