Laravel 4.1 eloquent model set appends dynamically - laravel

I am using Laravel 4.2.
I have two models: User and Video, both of these models are having one-to-many relationship i.e. User -> HasMany-> Video.
Recently, I got a requirement to display the list of users along with sum of file-size of total videos uploaded by each user and allow users to be order by the sum of file size ascending or descending.
I've made following changes in User model:
class User extends Eloquent {
protected $hidden = array('videosSum');
protected $appends = array('videos_size');
public function videosSum() {
return $this->hasOne('Video')
->selectRaw('sum(file_size) as sum, user_id')
->groupBy('user_id');
}
public function getVideosSizeAttribute()
{
// if relation is not loaded already, let's do it first
if ( ! array_key_exists('videos_size', $this->relations)){
$this->load('videosSum');
}
$related = $this->getRelation('videosSum');
return $this->attributes['videos_size'] = isset($related->sum) ? (int) $related->sum : 0;
}
}
And using like:
User::where('id', '!=', Auth::user()->id);
I am getting the desired result.
But the problem is, I don't want the videos_size attribute everywhere, where the User model gets called. I want to set it dynamically.
I tried User::$appends = ['videos_size'] but it gives protected property cannot be set outsize of class error.
I also tried to make a method in User model which set the $appends if called, but it is also not working.
Can anybody help me how to enable the appends property dynamically?

Laravel doesn't support this off the bat.
my friend and I wrote this extention:
Dynamically hide certain columns when returning an Eloquent object as JSON?
basically you have to override your models.php toArray() method as appended attributes get calculated when you ask for the model in json or array form.
you can add to the trait that's in that link and use it or just put these methods in your respective model class.
public static function getStaticAppends() {
return self::$_appends;
}
public static function setStaticAppends(array $value) {
self::$_appends = $value;
return self::$_appends;
}
public static function getDefaultAppends() {
return with(new static)->getAppends();
}
public function getAppends(){
return $this->appends;
}
public function toArray() {
if (self::getStaticAppends()) {
$this->appends = self::getStaticAppends();
}
return parent::toArray();
}

Related

in model get specific attribute from belongsTo() relation without appending the whole collection

let's say that I want to get the author's name inside the book model
// app/Book.php
protected $appends = ['author_name'];
public function author() {
return belongsTo(Author::class, 'author_id');
}
public function getAuthorNameAttribute() {
return $this->author->name;
}
but this would append the whole author collection to the final book collection as well, that would bump up the loading time when trying to load like 100 books, right now I work it around by removing the author after getting the name like this
// app/Book.php
protected $appends = ['author_name'];
public function author() {
return belongsTo(Author::class, 'author_id');
}
public function getAuthorNameAttribute() {
$authorName = $this->author->name;
unset($this->author);
return $authorName;
}
is there a better way to do it? or did I miss any function from eloquent?
Cheers
Try to add following code in your app/Book.php
// app/Book.php
protected $hidden = ['author'];

Using an Eloquent relationship within an Eloquent accessor?

I am writing a Laravel application that manages training courses.
Each course is represented by a Course model.
A course can have many dates - these are represented by a CourseDate model, with a hasMany relationship between the two:
Each course also has a single "date template", which is a CourseDate, but with an "is_template" boolean set.
I want to create an accessor on the Course model that retrieves its date template.
The (relevant) code for each model is:
class Course extends Model {
public function getDateTemplateAttribute() {
$dates = $this->dates;
$filtered = $dates->where('is_template', true);
$template = $filtered->first();
return $template;
}
public function dates() {
$result = $this->hasMany( CourseDate::class );
return $result;
}
}
class CourseDate extends Model {
public function course() {
return $this->belongsTo( Course::class );
}
}
Then, in my controller, I have this:
// this block works absolutely perfectly
$course = Course::find(1);
$dates = $course->dates;
$working_date_template = $dates->where('is_template', true)->first();
// this one doesn't work at all and says "call to a member function first() on array"
$broken_date_template = $course->date_template;
Stepping through with xdebug in the broken code, the line $dates = $this->dates returns an empty array so everything else afterwards breaks.
Is this a limitation with the Laravel accessor/relationship system? Or am I just being dense and doing something wrong.
I worked this out just now.
I needed to use $this->dates() within the model itself as this returns the relationship and I can then filter it out accordingly using the where() method and other query builder methods.
This was, of course, mentioned in the Laravel documentation - I just didn't spot it.

laravel display only specific column from relation

I have read a few topics about this, but they managed to solve my problem partially ...
this is my controller
class DeskController extends BaseController{
public function getDeskUsers($deskId){
$user = DeskUserList::where(function($query) use ($deskId){
$query->where('deskId', $deskId);
})->with('userName')->get(array('deskId'));
if (!$user->isEmpty())
return $user;
return 'fail';
}
this is the model
class DeskUserList extends Eloquent {
protected $table = 'desk_user_lists';
public function userName(){
return $this->belongsTo('User', 'userId')->select(array('id','userName'));
}
}
the method getDeskUsers may returns ALL the DeskUserList table records, related with the User table record (on deskUserList.userId = User.id).
practically I want each record returned is composed of:
DeskUserList.deskId
User.userName
eg. [{"deskId":"1","user_name":antonio}]
What i get is
[{"deskId":"1","user_name":null}]
As you can see the user name is a null value...
BUT
if I edit my controller code:
->with('userName')->get(array('userId')); //using userId rather than deskId
then i get
[{"userId":"2","user_name":{"id":"2","userName":"antonio"}}]
By this way I still have two problem:
the userId field is twice repeated
I miss the deskId field (that I need...)
hope be clear, thanks for your time!
You need belongsToMany, no need for a model representing that pivot table.
I assume your models are Desk and User:
// Desk model
public function users()
{
return $this->belongsToMany('User', 'desk_user_list', 'deskId', 'userId');
}
// User model
public function desks()
{
return $this->belongsToMany('Desk', 'desk_user_list', 'userId', 'deskId');
}
Then:
$desks = Desk::with('users')->get(); // collection of desks with related users
foreach ($desks as $desk)
{
$desk->users; // collection of users for particular desk
}
// or for single desk with id 5
$desk = Desk::with('users')->find(5);
$desk->users; // collection of users
$desk->users->first(); // single User model

How to use a protected property in an Eloquent model without Laravel trying to save it to the database

In one of my models, I have an attribute named "slug". When the slug is changed, I need to record the original slug before updating it to the new one, so my model has a protected property "originalSlug". Before saving the model, I do something like this in my model:
protected $originalSlug;
public function customSave($newSlug){
$this->originalSlug = $this->slug;
$this->slug = $newSlug;
return $this->save();
}
Then I have an event that does other tasks using that originalSlug after a successful save.
The problem is Laravel is trying to save the originalSlug to the database though it isn't actually an attribute and doesn't have a database column. So it fails with the "Column not found" error.
What could I do to get Laravel to ignore that originalSlug property, or is there a better way I should be doing this?
If you want Eloquent to ignore a property, it needs to be accessible to set, otherwise __set will be called and Eloquent will treat it as an attribute.
You can alternatively use mutator for this.
So here's what you need:
public $originalSlug;
public function customSave($newSlug){
$this->originalSlug = $this->slug;
$this->slug = $newSlug;
return $this->save();
}
or:
protected $originalSlug;
public function customSave($newSlug){
$this->originalSlug = $this->slug;
$this->slug = $newSlug;
return $this->save();
}
public function setOriginalSlugAttribute($value)
{
$this->originalSlug = $value;
}
Then Eloquent will not set an originalSlug attribute , so it won't be saved to the db.
You can do that with events, like suggested in the comments, and I would suggest this way too.

Laravel 4.1: proper way to retrieve all morphedBy relations?

Just migrated to 4.1 to take advantage of this powerful feature.
everything seems to work correctly when retrieving individual 'morphedByXxxx' relations, however when trying to retrieve all models that a particular tag belongs to -- i get an error or no results.
$tag = Tag::find(45); //Tag model name = 'awesome'
//returns an Illuminate\Database\Eloquent\Collection of zero length
$tag->taggable;
//returns Illuminate\Database\Eloquent\Relations\MorphToMany Builder class
$tag->taggable();
//returns a populated Collection of Video models
$tag->videos()->get();
//returns a populated Collection of Post models
$tag->posts()->get();
My Tag Model class loooks like this:
class Tag extends Eloquent
{
protected $table = 'tags';
public $timestamps = true;
public function taggable()
{
//none of these seem to function as expected,
//both return an instance of MorphToMany
//return $this->morphedByMany('Tag', 'taggable');
return $this->morphToMany('Tag', 'taggable');
//this throws an error about missing argument 1
//return $this->morphToMany();
}
public function posts()
{
return $this->morphedByMany('Post', 'taggable');
}
public function videos()
{
return $this->morphedByMany('Video', 'taggable');
}
}
And the Post and Video models look like this:
class Post extends Eloquent
{
protected $table = 'posts';
public $timestamps = true;
public function tags()
{
return $this->morphToMany('Tag', 'taggable');
}
}
I am able to add/remove Tags to Posts and Videos as well as retrieve the related Posts, and Videos for any Tag -- however -- what is the proper way to retrieve all Models having the Tag name 'awesome'?
Was able to figure it out, would love to hear comments on this implementation.
in Tag.php
public function taggable()
{
return $this->morphToMany('Tag', 'taggable', 'taggables', 'tag_id')->orWhereRaw('taggables.taggable_type IS NOT NULL');
}
in calling code:
$allItemsHavingThisTag = $tag->taggable()
->with('videos')
->with('posts')
->get();
I just used this on Laravel 5.2 (not sure if it is a good strategy though):
Tag model:
public function related()
{
return $this->hasMany(Taggable::class, 'tag_id');
}
Taggable model:
public function model()
{
return $this->belongsTo( $this->taggable_type, 'taggable_id');
}
To retrieve all the inverse relations (all the entities attached to the requested tag):
#foreach ($tag->related as $related)
{{ $related->model }}
#endforeach
... sadly this technique doesn't offer eager load functionalities and feels like a hack. At least it makes it simple to check the related model class and show the desired model attributes without much fear to look for the right attributes on the right model.
I posted a similar question in this other thread as I am looking for relations not known in advance.

Resources