Cannot save hasOne() model relationship - php

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'));

Related

Update relationship inside relationship hasMany values

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
]);
}

Laravel : how to validate a form field according to possible values of a Model's attribute?

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

Laravel Update Related Model with push?

I am stuck on updating a eagerloaded model that has a "hasMany" relation.
I have one model like so:
class UserGroup extends Model
{
public function enhancements()
{
return $this->hasMany('App\UserGroupEnhancement');
}
}
My controller is passing $userGroup to the view like so:
$userGroup = $this->userGroup->with('enhancements')->whereId($id)->first();
and then in my view I have
#foreach($userGroup->enhancements as $enhancement)
<label>{{$enhancement->type}}</label>
<input class="form-control" name="enhancements[{{$enhancement->id}}][price]" value="{{$enhancement->price}}">
#endforeach
When updating, how do I update all of records in the enhancement relationship? It's passed back into multiple arrays. I am currently doing something like this.
public function update($id)
{
$userGroup = $this->userGroup->findOrFail($id);
$enhancement = \Input::get('enhancements');
if (is_array($enhancement)) {
foreach ($enhancement as $enhancements_id => $enhancements_price) {
$userGroup->enhancements()->whereId($enhancements_id)->update($enhancements_price);
}
}
}
Is there a way I can do this without needing the foreach loop? I see the push() method, but seems to only work on a single array.
There isn't a better way to do this. There is an Eloquent method called saveMany but it is used to create new records and not update. ExampleDoc:
$comments = [
new Comment(['message' => 'A new comment.']),
new Comment(['message' => 'Another comment.']),
new Comment(['message' => 'The latest comment.'])
];
$post = Post::find(1);
$post->comments()->saveMany($comments);
I would stick with your solution, you can even create a trait or a base Eloquent class and put that logic in a method so it can be used by all other models, if you ever need to.
Something like:
trait UpdateMany {
public function updateMany($updates, $relationshipName)
{
if (!empty($updates)) {
foreach ($updates as $update_id => $update) {
$this->{$relationshipName}()->whereId($update_id)->update($update);
}
}
}
}
Then attach to your model(s):
class UserGroup extends Model
{
use UpdateMany;
public function enhancements()
{
return $this->hasMany('App\UserGroupEnhancement');
}
}
And simply use as:
$userGroup = $this->userGroup->findOrFail($id);
$userGroup->updateMany(\Input::get('enhancements'), 'enhancements');

How to update a "one to many" relation in Laravel? (can't find it in the docs)

<?php
class User extends Eloquent {
public function tickets()
{
return $this->hasMany('Tickets')
}
}
class Tickets extends Eloquent {
public function User()
{
return $this->belongTo('User')
}
}
//and how to update a Ticket that belongs To User?
// and this dosen't work...Who can tell me the reason ,thanks !
$ticket = User::find(1)->tickets()->where('type','sometype')->get();
$ticket.update(array('status' => 0));
The update() query is documented quite plainly in the official docs:
User::find(1)->tickets()->where('type','sometype')->update(array('status' => 0));
I suspect the issue you're having is that the get()` query is returning multiple records. In order for your query to work, you'll need to treat the returned value as a collection if records, then iterate and operate on each record individually.
$tickets = User::find(1)->tickets()->where('type','sometype')->get();
foreach ($tickets as $ticket) {
$ticket->update(array('status' => 0));
}
Note that the correct syntax for invoking an Eloquent method requires a preceding arrow -> – not a dot (.).
$record.update(array('foo' => 'bar')); # Incorrect
$record->update(array('foo' => 'bar')); # CORRECT!
UPDATE:
Model names should always be singular, not plural. Therefore, the Ticket model should be defined as follows:
class Ticket extends Eloquent { // `Ticket`, not `Tickets`
public function User()
{
return $this->belongTo('User')
}
}

laravel 4 eloquent eager load relation count

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.

Categories