Add attribute to Laravel model - laravel

I have user model, and I want to add if this user is qualified or not, I have some query to determine that,
User.php
....
public function getIsQualifiedAttribute($value)
{
return $this->qualified()->first() ? true : false;
}
public function scopeQualified($query)
{
return $query->where("years_of_experience",">=",5);
}
and this is how I want to print it
{{$user->is_qualified}}
but I can't make it work, it keeps giving me wrong value.

Laravel accessors does not have parameter
try to change your method like this
public function getIsQualifiedAttribute()
{
return $this->qualified()->first() ? true : false;
}

You don't need to do another query to this same table if you already have the model instance you want to check an attribute for. Just have the accessor check the years_of_experience field of the model:
public function getIsQualifiedAttribute()
{
return $this->years_of_experience >= 5;
}
{{ $user->is_qualified ? 'Y' : 'N' }}
With your current setup you are doing a query to the database to get the first record which has years_of_experience >= 5 every time the accessor was hit. If any record in that table has years_of_experience >= 5 that will return true.

Related

Accessor Not Working Even Though Column Exists?

I have a column in the "products" table called "walkover_only". I can access this attribute easily anywhere with $product->walkover_only, but I can't change it with an accessor?
public function getWalkoverOnlyAttribute($value)
{
if (strpos($this->name, 'Test') !== false) {
return 0;
} else {
return $value;
}
}
I've tried even just return 0 with no if statement, and everything still just returns its normal value. It doesn't need $appends because I can access the value everywhere just fine, it's a real column. I just can't affect what it returns in any way.

How to make empty values have a default value?

Im trying to give model attribute values another value when they are blank,
At the moment this is what I'm working with in my model:
public function getAttribute($property){
if(blank($this->attributes[$property])){
return $this->attributes[$property] = '-';
}else{
return $this->attributes[$property];
}
}
It works, but I dont think this is the right way to do it.
Im looking for a proper way of doing this.
example:
lets say the value in the database is NULL,
I want it to show "-" when displaying, but I dont want to save "-" in the database.
(I also don't want to use "get...Value" mutators for every value)
Solution 1
Since PHP 7 there is a new feature called the Null Coalescing operator. It returns the first operator when it exists and is not NULL:
{{ $model->attribute ?? '-' }}
Which is the same as this:
{{ isset($model->attribute) ? $model->attribute : '-' }}
Solution 2
Another solution would be a little bit harder, but doable:
Create a base model to which you extend all the other models:
class BaseModel extends Model {
protected $emptyAttributes = [];
protected function getAttribute($property)
{
if (in_array($property, $this->emptyAttributes) && blank($this->attributes[$property])) {
return '-';
}
else {
return $this->attributes[$property];
}
}
}
Now extend all the models you want to this new class and create an array of 'attributes to replace':
class User extends BaseModel {
protected $emptyAttributes = ['name', 'email'];
}
This should automatically replace the attributes name and email when they are empty, NULL or a string of only spaces.
Side note:
You could also move the functionality to a trait (which could be a more elegant solution, but that's up to you).

Laravel 5.2: Accessor on a column with camel case in its name

Im trying to use a laravel accessor to modify the content of a column when retrieving it. Im doing it like this:
On my model:
public function getMyColumnAttribute($value)
{
//Modify the content
return $value.'tttt';
}
On my controller:
public function test()
{
return MyModel::select('my_column')->find(1);
}
This works perfectly when the column in my DB is named 'my_column', but my entire database use camel case, so the actual name of the column is myColumn and the accessor just dont work in that case, I mean, it does return the value of my column, but without the modification I did in the accessor. I think it must be a way to it but I cant find how.
Thanks for the help
EDIT
I just realized that while this doesnt work: (myColumn is of type time on a MySql DB)
public function test()
{
return MyColumn::select('myColumn')->find(1);
//returns {"myColumn":"08:00:00"}
}
This acctually do what I expect
public function test()
{
$result = MyColumn::select('myColumn')->find(1);
return $result->myColumn;
// returns 08:00:00tttt
}
accessor doesnt modify the column raw data, it is used on create a new Attribute
use Mutator, save the modifyed raw data to db
use Accessor, get the raw data and modify it and assign to a new attribute
in your case
public function getMyColumnAttribute()
{
//Modify the content
return $this->attributes['type_column_name_here'] . 'tttt';
}
public function test()
{
// myColumn = the column name
$result = MyColumn::select('myColumn')->find(1);
// this is return the column value 08:00:00
return $result->myColumn;
// this will return accessor value 08:00:00tttt
return $result->my_column;
}
getMyColumnAttribute will convert to my_column

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