Laravel get documents that has x value in append attribute - laravel

Case Scenario Model User that has append attribute called custom
this custom attribute might differ between one document to the other
for example
public function getCustomAttribute()
{
return $this->id % 2;//this is an example to showcase that it will be different but it's not my case
}
this will return 0 or 1 depending on if the id is even or odd
I want to get only users with the 1 value how can I achieve this in the query since I want to use pagination

As I understand your questions correct you can try to do like that:
protected $appends = ['custom'];
public function getCustomAttribute(): int
{
return (int) $this->attributes['id'] % 2;
}

Related

Eloquent how to pass parameters to relationship

The code I'm trying to fix looks like this. I have an Hotel class which is used in a query to get all hotels in an area but it doesn't discard those which are not available. There's a method inside which should be an accessor but it's not written the way I expected it to be:
public function isAvailableInRanges($start_date,$end_date){
$days = max(1,floor((strtotime($end_date) - strtotime($start_date)) / DAY_IN_SECONDS));
if($this->default_state)
{
$notAvailableDates = $this->hotelDateClass::query()->where([
['start_date','>=',$start_date],
['end_date','<=',$end_date],
['active','0']
])->count('id');
if($notAvailableDates) return false;
}else{
$availableDates = $this->hotelDateClass::query()->where([
['start_date','>=',$start_date],
['end_date','<=',$end_date],
['active','=',1]
])->count('id');
if($availableDates <= $days) return false;
}
// Check Order
$bookingInRanges = $this->bookingClass::getAcceptedBookingQuery($this->id,$this->type)->where([
['end_date','>=',$start_date],
['start_date','<=',$end_date],
])->count('id');
if($bookingInRanges){
return false;
}
return true;
}
I wanted to filter out hotels using this query. So this is the query from the controller:
$list = $model_hotel->with(['location','hasWishList','translations','termsByAttributeInListingPage'])->get();
Is it possible to pass the range of days to the function?
By the way the first thing I tried was to use the collection after the query and pass a filter function through the collection and after that paginate manually but although it does filter, but apparently it loses
the "Eloquent" result set collection properties and it ends up as a regular collection, thus it doesn't work for me that way.
Maybe the best approach for that is to create a query scope (source) and put all your logic inside of this function.
after that you can call this scope and pass the dates. Example you will create a query scope and paste your code inside of it.
public function scopeisAvailableInRanges($query, $start_date, $end_date) {
}
then you will invoke this query scope in your controller like this.
$list = $model_hotel::isavailableinranges($start_date, $end_date)->with(['location','hasWishList','translations','termsByAttributeInListingPage'])->get();
keep in mind that inside of your query scope you will return a collection. A collection of all your available hotels.

Laravel 5 Pivot Table Extra Field

What I’m trying to do is when a new candidate is created an extra field is automatically populated in the joining pivot table with a random sting.
Here is my pivot table:
Result Table (pivot)
id cert_number candidate_id qualification_id
1 ? 17 2
2 ? 17 1
3 ? 57 1
So in my candidate controller I have:
public function store(CandidateRequest $request)
{
$candidateInput = Input::get('candidates');
foreach ($candidateInput as $candidate)
{
$candidate = Candidate::create($candidate);
$candidate->centre()->attach(Auth::user()->centre);
$qualification_id = $request->input('qualification_id');
$candidate->qualification()->attach($qualification_id);
$cert_number = Str::random(10);
$candidate->qualification()->attach($cert_number);
}
return redirect('candidates');
}
It adds the centre_id and qualification_id perfectly but it won’t pull though the random sting into the cert_nubmer field.
In my Candidate model I have
public function result()
{
return $this->hasMany('App\Result')->withTimestamps();
}
public function qualification()
{
return $this->belongsToMany('App\Qualification', 'Results', 'candidate_id', 'qualification_id')->withPivot('status','cert_number','partial_claim')->withTimestamps();
}
and in my result model:
public function candidate()
{
return $this->belongsTo('App\Candidate')->withTimestamps();
}
public function qualification()
{
return $this->belongsTo('App\Qualification');
}
Can anyone see where Im going wrong?
Thanks.
attach does not work like that.
Let me take out two lines of code:
$candidate->qualification()->attach($qualification_id);
$candidate->qualification()->attach($cert_number);
You are trying to add the $cert_number as a qualification_id, which is why it fails. When you do this, how is Laravel supposed to know that the second line ($cert_number) is an extra pivot column? It doesn't. You have two lines of code that are exactly the same so you can't expect Laravel to know that the second line should do something different.
When you want to insert extra data into other pivot columns, you need to pass them as an array in the second argument. Something like this:
$candidate->qualification()->attach($qualification_id, ['cert_number' => $cert_number]);

Retrieving records from database using eloquent with optional query parameters

i have the following block of code in my Resource Controller:
$travel_company_id = Input::get('travel_company_id');
$transport_type = Input::get('transport_type');
$route_type = Input::get('route_type');
$travelRoutes = TravelRoute::where('travel_company_id', $travel_company_id)
->where('transport_type', $transport_type)
->where('route_type', $route_type)
->get();
Now what this does is it gets travelRoutes based on the parameters supplied. What i want is for it to do is perform a search based on the available parameters, that way if $route_type is empty the search will be performed only on travel_company_id and transport type.
Also if all the parameters are empty then it will simply do a get and return all available records.
I know i can do this with lots of if statements but then if i add a new parameter on the frontend i will have to add it to the backend as well, I was wondering if there was a much simpler and shorter way to do this in laravel.
The where method accepts an array of constraints:
$constraints = array_only(Input::all(), [
'travel_company_id',
'transport_type',
'route_type',
]);
$routes = TravelRoute::where($constraints)->get();
Warning: do not use Input::only() instead of array_only(). They're not the same.
Input::only() fills in any missing items with null, which is not what you want here.
This is pretty hacky and if you spend some time developing a solution I'm sure it could be much nicer. This assumes all the fields in the getSearchFields() function match the input names from the form and database.
/**
* Search fields to retrieve and search the database with. Assumed they match the
* column names in the database
*/
private function getSearchFields()
{
return ['travel_company_id', 'transport_type', 'route_type'];
}
public function search()
{
// Get a new query instance from the model
$query = TravelRoute::query();
// Loop through the fields checking if they've been input, if they have add
// them to the query.
foreach($this->getSearchFields() as $field)
{
if (Input::has($field))
{
$query->where($field, Input::get($field));
}
}
// Finally execute the query
$travelRoutes = $query->get();
}

One to many relationship count - difference in accessing relationship

I have one to many relation - Entry can have many Visits.
In my Entry model I have the following methods:
public function visits() {
return $this->hasMany ('Visit', 'entry_id','id');
}
public function visitsCount() {
return $this->hasMany('Visit', 'entry_id','id')
->selectRaw('SUM(number) as count')
->groupBy('entry_id');
}
In Blade I can get number of visits for my entry using:
{{$entry->visits()->count() }}
or
{{ $entry->visitsCount()->first()->count }}
If I want to create accessor for getting number of visits I can define:
public function getNrVisitsAttribute()
{
$related = $this->visitsCount()->first();
return ($related) ? $related->count : 0;
}
and now I can use:
{{ $entry->nr_visits }}
Questions:
In some examples I saw defining such relation this way:
public function getNrVisitsAttribute()
{
if (!array_key_exists('visitsCount', $this->relations)) {
$this->load('visitsCount');
}
$related = $this->getRelation('visitsCount')->first();
return ($related) ? $related->count : 0;
}
Question is: what's the difference between this and the "simple method" I showed at the beginning? Is it quicker/use less resource or ... ?
Why this method doesn't work in this case? $related is null so accessor return 0 whereas using "simple method" it returns correct number of visits
I've tried also changing in visitsCount method relationship from hasMany to hasOne but it doesn't change anything.
1 Your relation won't work because you didn't select the foreign key:
public function visitsCount() {
// also use hasOne here
return $this->hasOne('Visit', 'entry_id','id')
->selectRaw('entry_id, SUM(number) as count')
->groupBy('entry_id');
}
2 Your accessor should have the same name as the relation in order to make sense (that's why I created those accessors in the first place):
public function getVisitsCountAttribute()
{
if ( ! array_key_exists('visitsCount', $this->relations)) $this->load('visitsCount');
$related = $this->getRelation('visitsCount');
return ($related) ? $related->count : 0;
}
This accessor is just a handy way to call the count this way:
$entry->visitsCount;
instead of
$entry->visitsCount->count;
// or in your case with hasMany
$entry->visitsCount->first()->count;
So it has nothing to do with performance.
Also mind that it is not defining the relation differently, it requires the relation to be defined like above.
Assuming your schema reflects one record / model per visit in your visits table, The best method would be to get rid of the visitsCount() relation and only use $entry->visits->count() to retrieve the number of visits to the entry.
The reason for this is that once this relation is loaded, it will simply count the models in the collection instead of re-querying for them (if using a separate relationship)
If your concern is overhead and unnecessary queries: My suggestion would be to eager-load these models in a base controller somewhere as children of the user object and cache it, so the only time you really need to re-query for any of it is when there have been changes.
BaseController:
public function __construct(){
if(!Cache::has('user-'.Auth::user()->id)){
$this->user = User::with('entries.visits')->find(Auth::user()->id);
Cache::put('user-'.Auth::user()->id, $this->user, 60);
} else {
$this->user = Cache::get('user-'.Auth::user()->id);
}
}
Then set up an observer on your Entry model to flush the user cache on save. Another possibility if you are using Memcached or Reddis would be to use cache tags so you don't have to flush the whole user's cache every time an Entry model is added or modified.
Of course, this also assumes that each Entry is related to a user, however, if it isn't and you need to use Entry alone as the parent, the same logic could apply, by moving the Cache class calls in your EntryController

Laravel Has One Relation changing the identifier value

I'm not sure this is a real relation. I will try to explain the best way I can.
So first of all, I have three models :
Appartement,
AppartementPrice
The AppartementPrice depends on :
- appartement_id
I would like the AppartementPrice to be retrieve like that :
If there is a specific price for the appartement, then retrieve it, If not retrieve the price for all appartement which is stored in the database with an appartement_id = 0.
So basically what I would like is to do something like that :
public function price()
{
if(isset($this->hasOne('AppartementPrice')->price) // Check that relation exists
return $this->hasOne('AppartementPrice');
else
return $this->hasOne('AppartementPrice')->where('appartement_id', '0');
}
But this is not working.
It does not retrive me the default price.
I guess anyway this is not a best practice ?
I first tried to get the informations like that :
//Check if appartment has a specific price or retrieve default
if($priceAppartement = AppartementPrice::getPriceByCompanyAppartement($this->id))
return $priceAppartement;
else
return AppartementPrice::getDefaultPrice();
But I had this error :
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
when doing :
echo $app->price->price;
How can I check that a relation exists ? And is there a way to do as I describe ?
Thank you
You can't replace relation like this, as what you intend is not logical - you want to retrieve relation that doesn't exist.
Instead you can do this:
public function getPriceAttribute()
{
return ($this->priceRelation) ?: $this->priceDefault();
}
public function priceDefault()
{
// edit: let's cache this one so you don't call the query everytime
// you want the price
return AppartmentPrice::remember(5)->find(0);
}
public function priceRelation()
{
return $this->hasOne('AppartementPrice');
}
Then you achieve what you wanted:
$app->price; // returns AppartmentPrice object related or default one
HOWEVER mind that you won't be able to work on the relation like normally:
$price = new AppartmentPrice([...]);
$app->price()->save($price); // will not work, instead use:
$app->priceRelation()->save($price);
First of all something really important in Laravel 4.
When you do not use parentheses when querying relationship it means you want to retreive a Collention of your Model.
You have to use parentheses if you want to continue your query.
Ex:
// for getting prices collection (if not hasOne). (look like AppartementPrice)
$appartment->price;
// for getting the query which will ask the DB to get all
//price attached to this appartment, and then you can continue querying
$priceQuery = $appartment->price();
// Or you can chain your query
$appartment->price()->where('price', '>', 0)->get() // or first() or count();
Secondly, your question.
//Appartement Model
// This function is needed to keep querying the DB
public function price()
{
return $this->hasOne('AppartementPrice')
}
// This one is for getting the appartment price, like you want to
public function getAppartmentPrice()
{
$price_object = $this->price;
if (!$price_object) // Appartment does not have any price {
return AppartementPrice->where('appartement_id', '=', 0)->get();
}
return $price_object;
}

Resources