Laravel factory seeding error - php

I have four tables Book, Author, User, Rating where Author and User table have many to many relationship with Book table, and Rating table has a foreign key with book and user table. 5 Authors and users are created, now I want to create book and rating table together where user_id and book_id will be same for book and rating table! I'm trying this way but this give me error in Rating factory! May be variable scope! How to solve this?
'user_id' => $userRandom,
class BooksTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
factory(App\Book::class, 2)->create()->each(function ($book) {
$author = App\Author::all();
$user = App\User::all();
$userRandom = $user->random();
factory(App\Rating::class)->create([
'book_id' => $book->id,
'user_id' => $userRandom,
]);
$book->authors()->save($author->random());
$book->users()->save($userRandom);
});
}
}

1) Make sure that:
'user_id' => $userRandom // keeps exact id.
Otherwise You'll need to pass it like:
'user_id' => $userRandom->id
2) Make sure that You've defined $fillable attribute of Rating model.
You cannot just do create and pass some array (assoc) if You've not defined field names in $fillable.
You may also use the create method to save a new model in a single line.
The inserted model instance will be returned to you from the method.
However, before doing so, you will need to specify either a fillable or guarded attribute on the model, as all Eloquent models protect against mass-assignment by default.
So, to get started, you should define which model attributes you want to make mass assignable.
You may do this using the $fillable property on the model.
Read about Mass Assignment https://laravel.com/docs/5.4/eloquent

Related

accessor considered as foreign key defined in OneToMany relations

My User model have these fields :
user_id
username
name
family
supervisor
And in that model I defined an accesssor that same name as supervisor attribute like this (because I want to return supervisor user as an User object and not a simple id):
public function getSupervisorAttribute($value)
{
return is_null($value) ? null : User::select('user_id', 'name', 'family')->find($value);
}
In the other hand there is a OneToMany relationship like this:
public function child()
{
return $this->hasMany(self::class, 'supervisor', 'user_id');
}
Now each time I call child() relation it return Illegal offset type error. seems that supervisor field does not recognized in second argument of hasMany method.
There is any way to solve this problem Without having to change accessor name.
I think the problem comes when you try to retrieve the relationship child, why? Because you have an accessor on your supervisor which is a foreign key inside of child relationship, so what happens is when you ask for that relationship, Laravel will try to use your supervisor property, since it has an accessor, it will trigger and instead of getting a desired property (which i guess is an integer), you will either get NULL or a User. I hope this clarifies it for you.
One workaround for this is to add appends attribute to your Model and then put mutators and accessors on that attribute.
If a User has children then it's one to many(he/she can have many children or none)
Anyway,
Lets assume you have a table named Children make sure you change the table name in the model(laravel assumes it's childrens in the DB).
If public function child() {} is in the User model then,
/*
* children since he/she can have many children
* hasMany means this model has many of the other model by self::class
* it's as if you're saying this model has many of this model so change it
*/
public function children()
{
/* you're building a relationship between User('user_id' as local primary key)
* and Children('parent_id' as foreign key)
* means children table has foreign key parent_id(unsignedInt)
* it returns an array of all the children objects of this User row
*/
return $this->hasMany('Children', 'parent_id', 'user_id');
}
On the other hand the Children Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Children extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'children';
public function parent()
{
// belongsTo means 'parent_id' in this model(Children) relates to 'user_id' on 'User' model
// it returns the User object which is the parent of this child row
return $this->belongsTo('User', 'user_id', 'parent_id');
}
}
This solution is for creating another table however it seems you want it with the same table(it's not very clear edit your post).
// this function makes no sense, it takes an integer and finds the parameter to was given
$userWithIdOne = $user->getSupervisorAttribute(1);
Give us the migrations of the table, show us the relationships.

#ORM\OneToMany Is retrieving the virtual deleted entries

I have a relation in Doctrine2 #ORM\OneToMany, suposing that i have table school and student, in the entity school i have the #ORM\OneToMany column students,
and i also have a virtual deletion column deleted_at, so every student that has the deleted_at different of null is a deleted student that is supposed not to appear in the column #ORM\OneToMany $students. How can i make this filter?
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToMany(targetEntity="App\Oceano\Entities\Student",
* mappedBy="cartCore",
* cascade={"all"}
* )
*/
private $students;
So, when i call for school students, it is retrieving also the deleted ones.
$schoolObj->getStudents();
Any Solution using annotation or some clean change?
You practically described Laravel's soft deleting feature. So, if you use it, you do not need to do anything and soft deleted students will not appear. You just need to add Illuminate\Database\Eloquent\SoftDeletes trait to the Student model.
If you're using some own functionality, create a local scope in the Student model:
public function scopeNotDeleted($query)
{
return $query->whereNull('deleted_at');
}
And use it:
Student::notDeleted()->get();
Or:
$school->students()->notDeleted()->get();
You can use Criteria filter class in your entity to students collection which are not deleted
protected getStudents() {
$criteria = \Doctrine\Common\Collections\Criteria::create()
->where(\Doctrine\Common\Collections\Criteria::expr()->eq('deleted_at', null));
return $this->students->matching($criteria);
}
To get deleted students you could write it like
protected getDeletedStudents() {
$criteria = \Doctrine\Common\Collections\Criteria::create()
->where(\Doctrine\Common\Collections\Criteria::expr()->neq('deleted_at', null));
return $this->students->matching($criteria);
}
How filter data inside entity object in Symfony 2 and Doctrine

Model store with different column than primaryKey

I have this Laravel/OctoberCMS Model to interface with my ExchangeModel.
It's a one sided relationship with my ExchangeModel.
Currently it stores in the Customer database table via ExchangeModel.id. But i'd like it to store as ExchangeModel.currencyISO, which is a 3 letter country code.
What do I need to setup in the relation to get laravel to store it via the other field I try to define via otherKey or key instead of the primaryKey(id)?
<?php namespace PhunTime\Client\Models;
use Model;
/**
* Customer Model
*/
class Customer extends Model {
/**
* #var string The database table used by the model.
*/
public $table = 'CUSTOMER';
public $primaryKey = 'customerNumber';
/**
* #var array Relations
*/
public $belongsTo = ['currency' => ['PhunTime\ExchangeRate\Models\ExchangeModel',
'key' => 'currencyISO',// EUR,USD,YEN
'otherKey' => 'currencyISO'
]];
}
Clarification of the structure:
- Customer
- id
- name
- currency(varchar(3)) <-- relation
- ExchangeModel
- id
- currencyISO
Currently the ExchangeModel.id gets stored in Customer.currency
I want ExchangeModel.currencyISO to be stored in Customer.currency
Customer.currency is already a varchar field suitable to accept it.
Currently if I specify type:relation in fields.yaml it stores it by ExchangeModel.id no matter what I specify how it should store it. use of otherKey, foreignKey, key, nothing helps changing octobers mind.
Currently i'm using a type: dropdown that i populate via getcurrencyOptions() but in my feeling this is a less than ideal situation.

getting the value of an extra pivot table column laravel

I have a phone_models, phone_problems, and a phone_model_phone_problem pivot table. The pivot table has an extra column 'price'.
PhoneModel:
class PhoneModel extends \Eloquent
{
public function problems()
{
return $this->belongsToMany('RL\Phones\Entities\PhoneProblem')->withPivot('price');
}
}
PhoneProblem:
class PhoneProblem extends \Eloquent
{
public function models()
{
return $this->belongsToMany('PhoneModel')->withPivot('price');
}
}
What I'm trying to do is get the price of a specific phone with a specific problem.
This is how I have it now but I feel like Laravel has a built in Eloquent feature I can't find to do this in a much simpler way:
$model = $this->phoneService->getModelFromSlug($model_slug);
$problem = $this->phoneService->getProblemFromSlug($problem_slug);
all this does is select the specific model and problem from their slug.
then what I do is with those credentials I get the price like so:
$row = DB::table('phone_model_phone_problem')
->where('phone_model_id', '=', $model->id)
->where('phone_problem', '=', $problem->id)
->first();
so now I can get the price like so $row->price but I feel like there needs to be a much easier and more 'Laravel' way to do this.
When using Many to Many relationships with Eloquent, the resulting model automatically gets a pivot attribute assigned. Through that attribute you're able to access pivot table columns.
Although by default there are only the keys in the pivot object. To get your columns in there too, you need to specify them when defining the relationship:
return $this->belongsToMany('Role')->withPivot('foo', 'bar');
Official Docs
If you need more help the task of configuring the relationships with Eloquent, let me know.
Edit
To query the price do this
$model->problems()->where('phone_problem', $problem->id)->first()->pivot->price
To get data from pivot table:
$price = $model->problems()->findOrFail($problem->id, ['phone_problem'])->pivot->price;
Or if you have many records with different price:
$price = $model->problems()->where('phone_problem', $problem->id)->firstOrFail()->pivot->price;
In addition.
To update data in the pivot you can go NEW WAY:
$model->problems()->sync([$problemId => [ 'price' => $newPrice] ], false);
Where the 2nd param is set to false meaning that you don't detach all the other related models.
Or, go old way
$model->problems()->updateExistingPivot($problemId, ['price' => $newPrice]);
And remind you:
To delete:
$model->problems()->detach($problemId);
To create new:
$model->problems()->attach($problemId, ['price' => 22]);
It has been tested and proved working in Laravel 5.1 Read more.
Laravel 5.8~
If you want to make a custom pivot model, you can do this:
Account.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Account extends Model
{
public function users()
{
return $this->belongsToMany(User::class)
->using(AccountUserPivot::class)
->withPivot(
'status',
'status_updated_at',
'status_updated_by',
'role'
);
}
}
AccountUserPivot.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class AccountUserPivot extends Pivot
{
protected $appends = [
'status_updated_by_nice',
];
public function getStatusUpdatedByNiceAttribute()
{
$user = User::find($this->status_updated_by);
if (!$user) return 'n/a';
return $user->name;
}
}
In the above example, Account is your normal model, and you have $account->users which has the account_user join table with standard columns account_id and user_id.
If you make a custom pivot model, you can add attributes and mutators onto the relationship's columns. In the above example, once you make the AccountUserPivot model, you instruct your Account model to use it via ->using(AccountUserPivot::class).
Then you can access everything shown in the other answers here, but you can also access the example attribute via $account->user[0]->pivot->status_updated_by_nice (assuming that status_updated_by is a foreign key to an ID in the users table).
For more docs, see https://laravel.com/docs/5.8/eloquent-relationships (and I recommend press CTRL+F and search for "pivot")

Laravel Eloquent saving an object with children

I am trying to save an order with order_items but I am not really finding anything in the docs to support this use case. A hasMany relationship.
Basically there is an orders table with something like id | user_id and an order_items table with id | order_id | product_id.
How can I save() the order and use an array of items at the same time without having to loop over the items and save them individually?
Is this possible?
Pseudo code assuming $items is an array:
$items = Session::get("cart.items");
$order = new Order;
$order->user_id = Auth::user()->id;
$order->order_items = $items;
$order->save();
What you need for a hasMany relation is either saveMany or createMany, depending on what's in your $items array:
// array of attributes:
$items = [
['name'=>'item1','price'=>'price1'],
...
];
// then createMany:
$order->orderItems()->createMany($items);
This will create new rows in Items table.
// array of models:
$items = [
Item::find($someId),
Item::find($anotherId),
// and/or newly instantiated:
new Item(['name'=>'item1','price'=>'price1']),
...
];
// then createMany:
$order->orderItems()->saveMany($items);
This will associate (save) existing models, and create non-existing ones.
Also notice that I use camelCase relation name orderItems instead of your order_items.
This is an important detail, since Eloquent (Laravel v4) looks for camelCased methods on the model when working with relations (dynamic properties).
//Order model
public function orderItems()
{
return $this->hasMany(...);
}
$order->orderItems; // collection
$order->order_items; // collection as well
// --------------------
// BUT
public function order_items()
{
return $this->hasMany(...);
}
$order->orderItems; // null
$order->order_items; // null
// the only way you can work with relation then, is explicitly use method like:
$order->order_items()->get();
Probably not the best solution you are looking for, but this should work.
Let's say that the array is named $items, I'm under the impression that you will be saving it into a pivot table. In my example below I also have a 3rd field on item_order pivot table named item_quantity.
foreach ($items as $item)
{
$order->items()
->attach($item['item_id'], ['item_quantity' => $item['item_quantity']]);
}
Basically you will be looping through the $items array. This will assume that you have defined the relationship on your Order model called items().
Then use the attach() method
->attach([insert the item_id], array('3rd field name' => 'value to be inserted')
Finally, if you don't have a 3rd field on your pivot table you could just do
->attach($item_id)
You can check the example given at the Laravel docs
Note
attach() is the method used when the you are only creating a record on the Database, otherwise you need a different method when you want to update.
#jareks answer helped in a similar scenario except for a mass assignment exception . so on digging up docs i found that you need to set a guarded or fillable property for mass assignment in latest versions of laravel (4.2) .
please refer this along with his answer .
Fillable or guarded properties
When creating a new model, you pass an array of attributes to the model constructor. These attributes are then assigned to the model via mass-assignment. This is convenient; however, can be a serious security concern when blindly passing user input into a model. If user input is blindly passed into a model, the user is free to modify any and all of the model's attributes. For this reason, all Eloquent models protect against mass-assignment by default.
So set the fillable or guarded properties on your model. Docs and Source
class User extends Eloquent {
protected $fillable = array('first_name', 'last_name', 'email');
}

Categories