Laravel: hasMany relationship breaks query with where clause - laravel

This is my first Laravel project, so maybe I'm misunderstanding something.
I have Clients and Campaigns contollers, and clients are setup with hasMany campaigns
class Client extends Model
{
public function campaigns(){
return $this->hasMany('App\Campaign');
}
}
in the Clients Show method, this query doesn't work:
$client = Client::find($id);
$campaigns = $client->campaigns
->where(function($query) use($startdate, $enddate) {
$query->wherebetween('start_date', [$startdate, $enddate])
->orwherebetween('end_date', [$startdate, $enddate]);
})
->sortBy('start_date')
->groupBy(function($val) {
return Carbon::parse($val->start_date)->format('F Y');
});
I get the error:
explode() expects parameter 2 to be string, object given
The error page shows a red line on the }) after the orwhereBetween clause.
But this query does work:
$campaigns = DB::table('campaigns')
->where('client_id', $client->id)
->where(function($query) use($startdate, $enddate) {
$query->wherebetween('start_date', [$startdate, $enddate])
->orwherebetween('end_date', [$startdate, $enddate]);
})
->orderBy('start_date')
->get()
->groupBy(function($val) {
return Carbon::parse($val->start_date)->format('F Y');
});
My problem is, the second query doens't let me access $campaign->tasks once I'm on the show view, which I need.
So, does anyone know why the where clause would cause an issue when accessing $client->campaigns but not when using DB?

That's because before you call get method the object is an instance of Eloquent Builder class and the groupBy method doesn't accepts any parameter as a closure. However after calling the get method it changes to an instance of Collection class which has groupBy method as well and it accepts closure.
By the way sortBy doen't belong to Eloquent builder instead you have to use orderBy before executing your query using get method.
Here are the available methods of Collection
https://laravel.com/docs/5.7/collections#available-methods
Try this code out and don't forget to call the methods camel case otherwise it can make you serious problems on deployment.
$dates = $client->campaigns()
->where(function($query) use($startdate, $enddate) {
$query->whereBetween('start_date', [$startdate, $enddate])
->orWhereBetween('end_date', [$startdate, $enddate]);
})
->orderBy('start_date')
->get()
->groupBy(function($val) {
return Carbon::parse($val->start_date)->format('F Y');
});
foreach($dates as $campaigns){
foreach($campaigns as $campaign){
dump($campaign->tasks);
}
}

Related

Laravel, eloquent, query: problem with retriving right data from DB

I'm trying to get the right data from the database, I'm retriving a model with a media relation via eloquent, but I want to return a photo that contains the 'main' tag stored in JSON, if this tag is missing, then I would like to return the first photo assigned to this model.
how i assign tags to media
I had 3 ideas:
Use orWhere() method, but i want more likely 'xor' than 'or'
$models = Model::with(['media' => function ($query) {
$query->whereJsonContains('custom_properties->tags', 'main')->orWhere();
}]);
return $models->paginate(self::PER_PAGE);
Raw SQL, but i don't really know how to do this i tried something with JSON_EXTRACT and IF/ELSE statement, but it was to hard for me and it was a disaster
Last idea was to make 2 queries and just add media from second query if there is no tag 'main'
$models = Model::with(['media' => function ($query) {
$query->whereJsonContains('custom_properties->tags', 'main');
}]);
$models_all_media = Model:: with(['media']);
return $models->paginate(self::PER_PAGE);
but i tried something like
for($i=0; $i<count($models); $i++) {
$models->media = $models_all_media
}
but i can't do this without get() method, beacuse i don't know how to change this to LengthAwarePaginator class after using get()
try using whereHas https://laravel.com/docs/9.x/eloquent-relationships
Model::with('media')
->whereHas('media',fn($media)=>$media->whereJsonContains('custom_properties->tags', 'main'))
->paginate(self::PER_PAGE);
as per your comment you can use
$models = Model::with(['media' => function ($query) {
$query->whereJsonContains('custom_properties->tags', 'main');
}])
->leftJoin('media', function ($join) {
$join->on('models.id', '=', 'media.model_id')
->whereNull('media.custom_properties->tags->main');
})
->groupBy('models.id')
->paginate(self::PER_PAGE);
return $models;

Laravel orderBy does not exist

I'm doing the simple query
$offers = Offer::join('offer_speciality', 'offers.id', 'offer_speciality.offer_id')
->join('specialities', 'offer_speciality.speciality_id', 'specialities.id')
->when($speciality, function($query, $speciality) {
return $query->where('specialities.number', $speciality)->get();
})
->orderBy('created_at','desc')->paginate(9);
and then error:
Method Illuminate\Database\Eloquent\Collection::orderBy does not
exist
I'm not sure what is it?
At the point of running ->get() a collection is being returned and ->orderBy() is not a function you can run on a collection with all the functions available being listed in the documention.
You've got your ->get() before your ->orderBy() and that wont work. Plus you don't need ->get() as you've got ->paginate(9)
You need:
$offers = Offer::join('offer_speciality', 'offers.id', 'offer_speciality.offer_id')
->join('specialities', 'offer_speciality.speciality_id', '=', 'specialities.id')
->when($speciality, function($query, $speciality) {
return $query->where('specialities.number', $speciality)
})
->orderBy('created_at','desc')->paginate(9)

whereBetween doesnt exist and i cant apply paginate

I've this method:
public function indexGuest($idAzienda, Request $request){
$companyId = $idAzienda;
$ddts = Ddt::where('company_id',$companyId);
$ddt_urls= Ddt_file_url::all();
if($request->start_date || $request->end_date){
$ddts->whereBetween('created_at',[new Carbon($request->start_date),new Carbon($request->end_date)]);
}
$ddts->paginate(10);
return view('guest.ddt-management')->with('ddts', $ddts)->with('ddt_urls',$ddt_urls)
->with('companyId',$companyId);
}
My start_date and end_date comes in strings like "yyyy-mm-dd".
I've tried to pass it straight to the query and like in the example like a carbon object with no hope!
After executing the query (now only the one without the wherebeeteween clause) i cant apply the method "paginate" to the collection, no error are raised but when i pass it to the view, the "link()" method not work and raise an error again.
where I wrong?
Laravel 5.4
Structure your wheres like this.
public function indexGuest($idAzienda, Request $request) {
[...]
$ddts = Ddt::where('company_id', $companyId)
->where(function($query) use ($request) {
if($s = $request->get("start_date") {
$s_date = Carbon::parse($s)->format("Y-m-d");
$query->whereDate("created_at", ">=", $s_date);
}
if($e = $request->get("end_date") {
$e_date = Carbon::parse($e)->format("Y-m-d");
$query->whereDate("created_at", "<=", $e_date);
}
})
->paginate(10);
[...]
}

How to add subqueries in eloquent db of laravel 5?

I am building an api in laravel 5.3 using eloquent if I use /api/location/event_name/event_status then I am getting results.
But when I use /api/location/event_name or /api/location/ nothing comes.
How to write the query so that all my link show result?
class events_api extends Controller
{
public function events($location = "",$event_name="",$event_status="")
{
$events = DB::table('event_table')
->join('event_details', 'event_table.event_id', '=', 'event_details.event_id')
->join('venue', 'event_details.venue_id', '=', 'venue.venue_id')
->where('venue.venue_city','=',$location)
->where('event_table.event_name','=','$event_name')
->where('event_table.event_status','=','$event_status')
->where('event_table.event_post_status','=','publish')
->select('event_table.event_title','event_details.event_start_ts','event_details.event_views','venue.venue_name','venue.venue_city','venue.venue_location')
->get();
echo $events;
}
}``
If you would like to make a subQuery try using toSql method:
class events_api extends Controller
{
public function events($location = "",$event_name="",$event_status="")
{
$subQuery = DB::table('event_table')
->join('event_details', 'event_table.event_id', '=', 'event_details.event_id')
->join('venue', 'event_details.venue_id', '=', 'venue.venue_id')
->where('venue.venue_city','=',$location)
->where('event_table.event_name','=','$event_name')
->where('event_table.event_status','=','$event_status')
->where('event_table.event_post_status','=','publish')
->select('event_table.event_title','event_details.event_start_ts','event_details.event_views','venue.venue_name','venue.venue_city','venue.venue_location');
DB::table(DB::raw("{$subQuery->toSql()} as main_query"))
->mergeBindings($subQuery->getBindings())
// build your query here
->get()
}
}
You'll also need to mergeBindings if you use any bindings in subquery

How to get relationship with union in eloquent?

How can I make relationship with union in laravel eloquent? I've already tried two different approaches.
User::with(['url' => function($query) use(&$some_property) {
$favouriteUrls = \DB::table('urls')
->select('urls.*')
->join('favourite_urls', function($join) {
$join->on('favourite_urls.url_id', '=', 'urls.id');
})
->where('some_condition', '=', $some_property);
$query = $query->union($favouriteUrls);
}]);
In the first attempt there wasn't any union in the query. Then I tried to move the logic to the model.
class User extends \Eloquent {
public function urls() {
$favouriteUrls = \DB::table('urls')
->select('urls.*')
->join('favourite_urls', function($join) {
$join->on('favourite_urls.url_id', '=', 'urls.id');
})
->where('some_condition', '=', $this->some_property);
return $this->belongsTo('Url')->union($favouriteUrls);
}
}
It has executed successfully but $this->some_property was set inside the query to the null value.
I can't create two separate relationship in this case. It has to be one with union. How can I fix it?
If you call that relation as User::with('urls') you will get that $this->some_property doesn't exists, because the object itself doesn't exists. But if you call the urls() method on an object, it should work. Something like this:
$user = User::find(1);
$user->urls; // here $this->some_property should have a value
Assuming you're calling the urls() method from a User object, the $this->some_property should give you the value. If for some reason you cannot access a property directly on an Eloquent model you can always refer to the attributes[] array inside of the model. For example
// calling
$this->some_property
// should be the same as
$this->attributes['some_property']
Fetch all the users joining with the condition
Assuming users is the table for all the users, in your query you could change $this->some_property with 'users.some_property' and everything should work as expected, for each user it will query based on that property. Here is the code:
class User extends \Eloquent {
public function urls() {
$favouriteUrls = \DB::table('urls')
->select('urls.*')
->join('favourite_urls', function($join) {
$join->on('favourite_urls.url_id', '=', 'urls.id');
})
->where('some_condition', '=', 'users.some_property');
return $this->belongsTo('Url')->union($favouriteUrls);
}
}
And then just call the method like this:
User::with('urls')->get();

Resources