So I was wondering how do I need to update relationship inside relationship values. For example I have first relationship:
class Code {
public function item()
{
return $this->hasOne(UserItem::class, 'code_id')
}
}
And then there is other nested relationship inside UserItem::class:
class UserItem {
public function serials()
{
return $this->hasMany(ItemSerial::class, 'user_item_id', 'id');
}
}
And then I recieve request values:
'item_serials' =>
array (
0 => 'test1',
1 => 'test2'
),
And I need to updateOrCreate values of serials relationship. Something like this:
foreach ($data['item_serials'] as $serial) {
$code->item->serials->updateOrCreate([
'serial' => $serial
]);
}
But this obviously doesn't work.
update or create method are static eloquent methods and for useing static methods from relationship you most use the relation method not property,so change your code to:
foreach ($data['item_serials'] as $serial) {
$code->item->serials()->updateOrCreate([
'serial' => $serial
]);
}
Related
I have a couple of models - let's call them Item and Ean. In Item model there are a couple of hasMany relations to the Ean model.
public function eans() {
return $this->hasMany(Ean::class)->orderBy('type', 'asc')->orderBy('id', 'asc');
}
public function eans_type_1() {
return $this->hasMany(Ean::class)->where('type', 1)->orderBy('id', 'asc');
}
public function eans_type_2() {
return $this->hasMany(Ean::class)->where('type', 2)->orderBy('id', 'asc');
}
When I want to associate a new Ean model to the Item, I'm using create() :
$item->eans()->create(['ean' => $value, 'type' => 1]);
or
$item->eans()->create(['ean' => $value, 'type' => 2]);
Is there a way to define hasMany relation in a way that I'll be able to do this :
$item->eans_type_1()->create(['ean' => $value]);
Not without some serious overriding. When you call
$items->eans()
$items->eans_type_1()
$items->eans_type_2()
You're getting an Illuminate\Database\Eloquent\Relations\HasMany instance and when you call
$items->eans
$items->eans_type_1
$items->eans_type_2
You're getting an Illuminate\Database\Eloquent\Collection instance.
Just pass the value.
I'm performing validation of a form, where a user may select a range of values (based on a set of entries in a model)
E.g. I have the Model CfgLocale(id, name)
I would like to have something like:
CfgLocale->listofAvailableIds() : return a array
What I did is:
Inside Model this method:
class CfgLocale extends Model
{
protected $table = 'cfg_locales';
public static function availableid()
{
$id_list = [];
$res = self::select('id')->get();
foreach($res as $i){
$id_list[] = $i->id;
}
return $id_list;
}
}
On Controller for validation I would do then:
$this->validate($request, [
'id' => 'required|integer|min:1',
...
'locale' => 'required|in:'.implode(',', CfgLocale::availableid()),
]);
Any better Idea, or Laravel standard to have this done?
Thanks
You can use exists rule of laravel.You can define a validation rule as below. Might be this can help.
'locale' => 'exists:cfg_locales,id'
Use this code instead,
class CfgLocale extends Model
{
protected $table = 'cfg_locales';
public static function availableid()
{
return $this->pluck('id')->toArray();
}
}
pluck method selects the id column from your table and toArray method converts your model object collection into array.
Know more about Laravel Collections here.
This will return an array of IDs:
public static function availableid()
{
return $this->pluck('id')->toArray();
}
https://laravel.com/docs/5.3/collections#method-pluck
https://laravel.com/docs/5.3/collections#method-toarray
Visitor model:
public function group()
{
return $this->belongsTo('MyApp\Models\VisitorGroup', 'group_id');
}
VisitorGroup model:
public function visitors()
{
return $this->hasMany('MyApp\Models\Visitor');
}
So then I'm trying to create some Visitors for a group:
$mygroup = VisitorGroup::whereRaw('name LIKE "%mygroup%"')->first();
foreach(range(1, 10) as $i)
{
$v = Visitor::create(array('name' => 'Homer simpson'));
$v->group()->save($mygroup); // HERE trying to add this visitor to the group
}
But I'm getting this error:
[BadMethodCallException]
Call to undefined method Illuminate\Database\Query\Builder::save()
Am I doing something wrong?
That's because BelongsTo has no save() method. However it has an associate() method which is probably what you're looking for. Not that you have to explicitly save the model afterwards:
$v = Visitor::create(array('name' => 'Homer simpson'));
$v->group()->associate($mygroup);
$v->save();
Or you could just set the foreign key manually when creating to save db queries:
$v = Visitor::create(array('name' => 'Homer simpson', 'group_id' => $mygroup->id));
Or the probably most elegant way:
$mygroup->visitors()->create(array('name' => 'Homer simpson'));
I've got 2 models with a many-to-many relationship. I want to be able to set a specific attribute with an array of ids and make the relationship in the mutator like this:
<?php
class Profile extends Eloquent {
protected $fillable = [ 'name', 'photo', 'tags' ];
protected $appends = [ 'tags' ];
public function getTagsAttribute()
{
$tag_ids = [];
$tags = $this->tags()->get([ 'tag_id' ]);
foreach ($tags as $tag) {
$tag_ids[] = $tag->tag_id;
}
return $tag_ids;
}
public function setTagsAttribute($tag_ids)
{
foreach ($tag_ids as $tag_id) {
$this->tags()->attach($tag_id);
}
}
public function tags()
{
return $this->belongsToMany('Tag');
}
}
<?php
class Tag extends Eloquent {
protected $fillable = [ 'title' ];
protected $appends = [ 'profiles' ];
public function getProfilesAttribute()
{
$profile_ids = [];
$profiles = $this->profiles()->get([ 'profile_id' ]);
foreach ($profiles as $profile) {
$profile_ids[] = $profile->profile_id;
}
return $profile_ids;
}
public function profiles()
{
return $this->belongsToMany('Profile');
}
}
However the setTagsAttribute function isn't working as expected. I'm getting the following error: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'profile_id' cannot be null (SQL: insert intoprofile_tag(profile_id,tag_id) values (?, ?)) (Bindings: array ( 0 => NULL, 1 => 1, ))
You can't attach many-to-many relations until you've saved the model. Call save() on the model before setting $model->tags and you should be OK. The reason for this is that the model needs to have an ID that Laravel can put in the pivot table, which needs the ID of both models.
It looks like you're calling the function incorrectly or from an uninitialized model. The error says that profile_id is NULL. So if you're calling the function as $profile->setTagsAttribute() you need to make sure that $profile is initialized in the database with an ID.
$profile = new Profile;
//will fail because $profile->id is NULL
//INSERT: profile->save() or Profile::Create();
$profile->setTagsAttribute(array(1,2,3));
Additionally, you can pass an array to the attach function to attach multiple models at once, like so:
$this->tags()->attach($tag_ids);
You can also pass it the model instead of the ID (but pretty sure array of models won't work)
Try using the sync method:
class Profile extends Eloquent {
protected $fillable = [ 'name', 'photo', 'tags' ];
protected $appends = [ 'tags' ];
public function getTagsAttribute()
{
return $this->tags()->lists('tag_id');
}
public function setTagsAttribute($tag_ids)
{
$this->tags()->sync($tagIds, false);
// false tells sync not to remove tags whose id's you don't pass.
// remove it all together if that is desired.
}
public function tags()
{
return $this->belongsToMany('Tag');
}
}
Don't access the tags through the tags() function, rather use the tags property. Use the function name if you want to pop additional parameters onto the relationship query and the property if you just want to grab the tags. tags() works in your getter because you're using get() on the end.
public function setTagsAttribute($tagIds)
{
foreach ($tagIds as $tagId)
{
$this->tags->attach($tagId);
}
}
I have a complex Model with multiple defined relations. In this example I would want to count the Like model and create a property named likes so it can be returned from a REST service.
Is it possible to eager load a model count into a dynamic property?
$beat = Post::with(
array(
'user',
'likes' => function($q){
$q->count();
}
))
->where('id', $id)
->first();
Assuming you are having Post->hasMany->Like relationship and you have declared likes relationship as:
class Post{
public function likes(){
return $this->hasMany('Like');
}
}
create a new function say likeCountRelation as:
public function likeCountRelation()
{
$a = $this->likes();
return $a->selectRaw($a->getForeignKey() . ', count(*) as count')->groupBy($a->getForeignKey());
}
now you can override __get() function as:
public function __get($attribute)
{
if (array_key_exists($attribute, $this->attributes)) {
return $this->attributes[$attribute];
}
switch ($attribute) {
case 'likesCount':
return $this->attributes[$attribute] = $this->likesCountRelation->first() ? $this->likesCountRelation->first()->count : 0;
break;
default:
return parent::__get($attribute);
}
}
or you can use getattribute function as :
public function getLikesCountAttribute(){
return $this->likesCountRelation->first() ? $this->likesCountRelation->first()->count : 0;
}
and simply access likesCount as $post->likesCount you can even eager load it like:
$posts=Post::with('likesCountRelation')->get();
foreach($post as $post){
$post->likesCount;
}
NOTE: Same logic can be used for morph many relationships.
You should use the SQL Group By statement in order to make it works. You can rewrite your query like the following one.
$beat = Post::with(
array(
'user',
'likes' => function($q) {
// The post_id foreign key is needed,
// so Eloquent could rearrange the relationship between them
$q->select( array(DB::raw("count(*) as like_count"), "post_id") )
->groupBy("post_id")
}
))
->where('id', $id)
->first();
The result of likes is a Collection object with one element. I'm assuming the relationship between model Post and Like is Post hasMany Like. So you can access the count like this.
$beat->likes->first()->like_count;
I'm not tested code above but it should works.