I'm trying to insert data from a Seeder in Laravel 5.6 and I'm having a problem with the field that is json type. I want this field ('stops') to be an array (for example of ten integers not repeated).
The table seeder (RoutesTableSeeder.php) is something like this:
<?php
use \Illuminate\Support\Facades\DB;
use Illuminate\Database\Seeder;
use Faker\Factory as Faker;
use App\Models\Route;
class RoutesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
//factory(Route::class, 20)->create();
$faker = Faker::create();
//$values= array();
/*for($i=0; $i < 10; $i++) {
$values []= $faker->unique()->randomDigit;
}
print_r(json_encode($values));*/
foreach (range(1, 20) as $index)
{
$values = array();
for($i=0; $i < 10; $i++) {
$values []= $faker->unique()->randomDigit;
}
//print_r($values);
DB::table('routes')->insert([
'user_id' => $faker->numberBetween($min = 1, $max = 20),
'name' => $faker->name,
'description' => $faker->name,
'route_photo' => $faker->image($dir = null, $width = 640, $height = 480, $category = null, $fullPath = true, $randomize = true, $word = null),
'stops'=> [
//$values,
json_encode($values)
//implode(", ", $values)
],
]);
}
}
}
I tried several ways to insert data. When I use json_encode($values) I have the following error:
Array to string conversion
(SQL: insert into `routes` (`user_id`, `name`, `description`, `route_photo`, `stops`)
values (19, Isaac
Feil, Holly Nolan, /tmp/bc8a3cf5e015d3afa96317485499e0ca.jpg,
[8,6,0,7,3,1,5,2,4,9]))
This kind of value [8,6,0,7,3,1,5,2,4,9] is what I want to store in 'stops' field, for example, but I don't know what is going wrong....
Please, would you be so kind to help me? I'm desperate....
I post the model if it helps:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Route extends Model
{
protected $fillable = [
'user_id',
'name',
'description',
'route_photo',
'stops'
];
protected $casts = [
'stops' => 'array'
];
}
And the migration:
public function up()
{
Schema::create('routes', function (Blueprint $table) {
$table->increments('id');
//FK:users
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
//FK:users
$table->string('name');
$table->string('description')->nullable();
$table->string('route_photo');
$table->json('stops');
$table->timestamps();
});
}
Thanks a lot!!
json_encode($values) returns a string, which you can use as the value of the stops column. There's no need to put [] around it, that creates an array, and you can't store an array directly into a column. Just leave out the brackets:
'stops' => json_encode($values)
However, storing arrays in database columns is generally a bad idea, it violates normalization principles. You should use a separate table with a row for each value.
Don't cast stops to array, First remove
protected $casts = [
'stops' => 'array'
];
And use json_encode to make string
'stops'=> json_encode($values),
Related
I am beginner in Laravel 7, I am using two tables 'empmast' and 'empatten'. I displayed the values of empmast (empid, empname) and joined two fields (empstatus, doa) with same. Then I tried to push these values to 'empatten' table. The thing is these values are trying to save in the empmast instaed empatten table. Kindly assist.
Complete Error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'empstatus' in 'field list'
(SQL: insert into `empmast` (`empid`, `empname`, `empstatus`, `doa`, `updated_at`, `created_at`) values (2, Kirupa Shankar, Present, 17-05-2020, 2020-05-17 06:34:26, 2020-05-17 06:34:26))
EmpAttenController:
use App\Empatten;
use App\Empmast;
use Illuminate\Http\Request;
class EmpAttenController extends Controller
{
public function store(Request $request, Empatten $empatten)
{
$member1 = $request->input('empid');
$member2 = $request->input('empname');
$member3 = $request->input('empstatus');
$member4 = $request->input('doa');
for ($i = 0; $i < count($member1); $i++) {
$empatten->empid = $member1[$i];
$empatten->empname = $member2[$i];
$empatten->empstatus = $member3[$i];
$empatten->doa = $member4;
$empatten->save();
}
}
}
Empatten(Model):
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Empatten extends Model
{
protected $fillable = [
'empid' => 'array',
'empname' => 'array',
'empstatus' => 'array',
'doa'
];
}
Create new instance of your model before you try to save
use App\Empatten;
use App\Empmast;
use Illuminate\Http\Request;
class EmpAttenController extends Controller
{
public function store(Request $request)
{
$member1 = $request->input('empid');
$member2 = $request->input('empname');
$member3 = $request->input('empstatus');
$member4 = $request->input('doa');
for ($i = 0; $i < count($member1); $i++) {
$empatten = new Empatten(); // initiate your model class
$empatten->empid = $member1[$i];
$empatten->empname = $member2[$i];
$empatten->empstatus = $member3[$i];
$empatten->doa = $member4;
$empatten->save();
}
}
}
I have several tables (all of them with created_at, updated_at, deleted_at) :
sectors
lang_sector
valuechains
lang_valuechain
segments
lang_segment
keyneeds
keyneed_lang
the tables are linked in this order :
sectors has many valuechains
valuechains has many segments
segments has many keyneeds
Here is my model :
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Sector extends Model
{
use SoftDeletes;
protected $table = "sectors";
protected $fillable = ['admin_id'];
protected $dates = [ 'created_at', 'updated_at', 'deleted_at' ];
public function langs() {
return $this->belongsToMany('App\Lang')
->withPivot('sectname', 'sectshortname', 'segname_slug',
'sectdescription', 'sectshortdescription'
)
->withTimestamps();
}
public function admin()
{
return $this->belongsTo('App\Admin');
}
public function valuechains()
{
return $this->hasMany('App\Valuechain');
}
public function segments()
{
return $this->hasManyThrough('App\Segment', 'App\Valuechain');
}
public function keyneeds()
{
return $this->hasManyThrough('App\Keyneed', 'App\Segment', 'App\Valuechain');
}
}
In my destroy controller :
public function destroy($id)
{
$sector = Sector::findOrFail($id);
$sector_ids = $sector->langs()->allRelatedIds();
foreach ($sector_ids as $id){
$sector->langs()->updateExistingPivot($id, ['lang_sector.deleted_at' => Carbon::now()]);
}
$sector->valuechains()->update( [ 'valuechains.deleted_at' => Carbon::now() ] );
$sector->segments()->update( [ 'segments.deleted_at' => Carbon::now() ] );
$sector->keyneeds()->update( [ 'keyneeds.deleted_at' => Carbon::now() ] );
Sector::where('id', $id)->delete();
return redirect()->route('sectors.index')->with('success', 'Sector deleted');
}
My issue is that it doesn't update the following tables : segments and keyneeds (which have created_at, updated_at and deleted_at fields) and their pivot tables too ... And that i have an error message :
SQLSTATE[23000]: Integrity constraint violation: 1052 Field: 'updated_at' in field list is ambiguous (SQL: update segments inner join valuechains on valuechains.id = segments.valuechain_id set segments.deleted_at = 2018-05-10 06:54:54, updated_at = 2018-05-10 06:54:54 where valuechains.sector_id = 2)
it adds : updated_at
I succesfully updated :
sectors table
valuechains table
lang_valuechain pivot
My issue is to update
segments table by using : $sector->segments()
keyneeds table by using : $sector->keyneeds()
And their pivot table as well... I read the documentation but it doesn't help.
I use the update() method because $sector->segments()->delete() is trying to make a hard delete...
I finally find an issue to this.. Not very elegant but it's working on the "main" tables. I'll have to solve the little problem on pivot tables :
public function destroy($id)
{
$sector = Sector::findOrFail($id);
$valuechains = Valuechain::where('sector_id','=',$sector->id)->get();
foreach ($valuechains as $valuechain) {
$segments = Segment::where('valuechain_id', '=', $valuechain->id )->get();
$valuechain->langs()->updateExistingPivot($valuechain->id, ['lang_valuechain.deleted_at' => Carbon::now()]);
foreach ($segments as $segment) {
$keyneeds = Keyneed::where('segment_id', '=', $segment->id)->get();
$segment->langs()->updateExistingPivot($segment->id, ['lang_segment.deleted_at' => Carbon::now()]);
$segment->delete();
foreach ($keyneeds as $keyneed) {
$keyneed->langs()->updateExistingPivot($keyneed->id, ['keyneed_lang.deleted_at' => Carbon::now()]);
$keyneed->delete();
}
}
$valuechain->delete();
}
$sector->langs()->updateExistingPivot($id, ['lang_sector.deleted_at' => Carbon::now()]);
$sector->delete();
return redirect()->route('sectors.index')->with('success', 'Secteur suppprimé');
}
I have one to many relation tables. I want to insert child table data in array instead of rows.
Mobile.php
protected $fillable = ['mobile_id', 'mobile_name'];
public function models() {
return $this->hasMany(Model::class, 'mobile_id');
}
Model.php
protected $fillable = ['model_name', 'mobile_id'
];
public function model()
{
return $this->belongsTo(Mobile::class, 'mobile_id');
}
View
Mobile Name: <input type="text" name="mobile_name">
Models:
<input type="text" name="model_name[]">
<button class="add_model">Add Model</button>
Controller
public function create(Request $request, Mobile $mob){
$mob= new Mobile;
$mob->mobile_name = request('mobile_name');
for($i=0; $i < count(request('model_name')); $i++){
$models = new Model;
$models->model_name = request('model_name')[$i];
$mob->models()->save($models);
}
$mob->save();
}
It's creates a new row for each mobile models inserted. But I want those models inserted in a single column model_name in an array line.
I tried
for($i=0; $i < count(request('model_name')); $i++){
Size::create([
'model_name' => request('model_name')[$i],
'mobile_id' => $mob->id
]);
But it is also not working.
I think the problem is your schema table, because you need to achieve this first :
// Migrations
$table->json('model_name');
That way you can persist many model_name in one row using one array.
You should serialize the data before saving it in the database.Create the model instance outside the for loop.Here is the working code.
public function create(Request $request, Mobile $mob){
$mob= new Mobile;
$mob->mobile_name = $request('mobile_name');
$models = new Model;
$arr=[];
for($i=0; $i < count($request('model_name')); $i++){
array_push($arr, $request('model_name')[$i]);
}
serialize($arr);
$models->model_name =$arr;
$mob->models()->save($models);
$mob->save();
}
In the model.php, add the below code
protected $casts = [
'model_name ' => 'array',
]
Try this 100% working i have deployed .
foreach($request->model_name as $item => $value)
$data[$value]=array(
'mobile_name'=>$request->mobile_name,
'model_name'=>$value,
);
Mobile::insert($data); // Eloquent approach
I have user input following the rules below;
public function rules()
{
return [
'phone_number' => 'required|array',
'amount' => 'required|string|max:4',
'phone_number_debit' => 'required|string|max:15',
];
}
I would want to save the data in a model Transaction. For the phone_number it is an array that could have one value or multiple. So that leaves for foreach loop.
This is what I want to achieve, save different rows determined by the number of records in the array.
$transaction = new Trasaction();
$transaction->phone_number = $req->phone_number; //Value in the array
$transaction->amount = $req->amount;
$transaction->phone_number_debit = $req->phone_number_debit;
$transaction->save();
Save diffrent records according to the records in the phone_number array.
However I can not think of a way to achieve this.
Anyone?
try this :
$data = request(['amount', 'phone_number', 'phone_number_debit']);
foreach($data['phone_number'] as $phone_number) {
Trasaction::create([
'amount' => $data['amout'],
'phone_number' => $phone_number,
'phone_number_debit' => $data['phone_number_debit']
]);
}
make sure in your Trasaction modal you've set to fillable property like this :
class Trasaction extends Model
{
protected $fillable = ['amount', 'phone_number', 'phone_number_debit'];
}
There are many ways to do this, in a nutshell:
collect(request('phone_number'))->each(function ($phone) use ($req) {
$transaction = new Trasaction();
$transaction->phone_number = $phone; // element of the array
$transaction->amount = $req->amount;
$transaction->phone_number_debit = $req->phone_number_debit;
$transaction->save();
});
TL;DR
One-to-Many Relationship
In order to get a better code, you can create a transaction_phones table, creating a one-to-many relationship.
You'll create a TransactionPhone model and add this:
public function transaction()
{
return $this->belongsTo(Transaction::class);
}
The TransactionPhone migration:
Schema::create('transaction_phones', function (Blueprint $table) {
$table->increments('id');
$table->integer('transaction_id');
$table->string('phone_number');
$table->timestamps();
});
In your Transaction model you'll have the inverse:
public function phones()
{
return $this->hasMany(TransactionPhone::class);
}
public function addPhone($phone)
{
return $this->phones()->create(['phone_number' => $phone]);
}
And in you Controller:
$transaction = Trasaction::create(request()->only('amount', 'phone_number_debit'));
collect(request('phone_number'))->each(function ($phone) use ($transaction) {
$transaction->addPhone($phone);
});
I hope this answer can help you.
In Laravel, database seeding is generally accomplished through Model factories. So you define a blueprint for your Model using Faker data, and say how many instances you need:
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'email' => $faker->email,
'password' => bcrypt(str_random(10)),
'remember_token' => str_random(10),
];
});
$user = factory(App\User::class, 50)->create();
However, lets say your User model has a hasMany relationship with many other Models, like a Post model for example:
Post:
id
name
body
user_id
So in this situation, you want to seed your Posts table with actual users that were seeded in your Users table. This doesn't seem to be explicitly discussed, but I did find the following in the Laravel docs:
$users = factory(App\User::class, 3)
->create()
->each(function($u) {
$u->posts()->save(factory(App\Post::class)->make());
});
So in your User factory, you create X number of Posts for each User you create. However, in a large application where maybe 50 - 75 Models share relationships with the User Model, your User Seeder would essentially end up seeding the entire database with all it's relationships.
My question is: Is this the best way to handle this? The only other thing I can think of is to Seed the Users first (without seeding any relations), and then pull random Users from the DB as needed while you are seeding other Models. However, in cases where they need to be unique, you'd have to keep track of which Users had been used. Also, it seems this would add a lot of extra query-bulk to the seeding process.
You can use saveMany as well. For example:
factory(User::class, 10)->create()->each(function ($user) {
$user->posts()->saveMany(factory(Posts::class, 5)->make());
});
You can do this using closures within the ModelFactory as discussed here.
This solution works cleanly and elegantly with seeders as well.
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'email' => $faker->email,
'password' => bcrypt(str_random(10)),
'remember_token' => str_random(10),
];
});
$factory->define(App\Post::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'body' => $faker->paragraph(1),
'user_id' => function() {
return factory(App\User::class)->create()->id;
},
];
});
For your seeder, use something simple like this:
//create 10 users
factory(User::class, 10)->create()->each(function ($user) {
//create 5 posts for each user
factory(Post::class, 5)->create(['user_id'=>$user->id]);
});
NOTE: This method does not create unneeded entries in the database, instead the passed attributes are assigned BEFORE the creation of associated records.
Personally I think one Seeder class to manage these relations is nicer then separated seeder classes, because you have all the logic in one place, so in one look you can see what is going on. (Anyone that knows a better approach: please share) :)
A solution might be: one DatabaseSeeder and private methods within the class to keep the 'run' method a bit cleaner. I have this example below, which has a User, Link, LinkUser (many-to-many) and a Note (many-to-one).
For the many-to-many relations I first create all the Links, and get the inserted ids. (since the ids are auto-inc I think the ids could be fetched easier (get max), but doesn't matter in this example). Then create the users, and attach some random links to each user (many-to-many). It also creates random notes for each user (many-to-one example). It uses the 'factory' methods.
If you replace the 'Link' for your 'Post' this should work. (You can remove the 'Note' section then...)
(There is also a method to make sure you have 1 valid user with your own login credentials.)
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
// Create random links
factory(App\Link::class, 100)->create();
// Fetch the link ids
$link_ids = App\Link::all('id')->pluck('id')->toArray();
// Create random users
factory(App\User::class, 50)->create()->each(function ($user) use ($link_ids) {
// Example: Many-to-many relations
$this->attachRandomLinksToUser($user->id, $link_ids);
// Example: Many-to-one relations
$this->createNotesForUserId( $user->id );
});
// Make sure you have a user to login with (your own email, name and password)
$this->updateCredentialsForTestLogin('john#doe.com', 'John Doe', 'my-password');
}
/**
* #param $user_id
* #param $link_ids
* #return void
*/
private function attachRandomLinksToUser($user_id, $link_ids)
{
$amount = random_int( 0, count($link_ids) ); // The amount of links for this user
echo "Attach " . $amount . " link(s) to user " . $user_id . "\n";
if($amount > 0) {
$keys = (array)array_rand($link_ids, $amount); // Random links
foreach($keys as $key) {
DB::table('link_user')->insert([
'link_id' => $link_ids[$key],
'user_id' => $user_id,
]);
}
}
}
/**
* #param $user_id
* #return void
*/
private function createNotesForUserId($user_id)
{
$amount = random_int(10, 50);
factory(App\Note::class, $amount)->create([
'user_id' => $user_id
]);
}
/**
* #param $email
* #param $name
* #param $password
* #return void
*/
private function updateCredentialsForTestLogin($email, $name, $password)
{
$user = App\User::where('email', $email)->first();
if(!$user) {
$user = App\User::find(1);
}
$user->name = $name;
$user->email = $email;
$user->password = bcrypt($password); // Or whatever you use for password encryption
$user->save();
}
}
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'email' => $faker->email,
'password' => bcrypt(str_random(10)),
'remember_token' => str_random(10),
];
});
$factory->define(App\Post::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'body' => $faker->paragraph(1),
'user_id' => factory(App\User::class)->create()->id,
];
});
So now if you do this factory(App\Post::class, 4)->create() it will create 4 different posts and in the process also create 4 different users.
If you want the same user for all the posts what I usually do is:
$user = factory(App\User::class)->create();
$posts = factory(App\Posts::class, 40)->create(['user_id' => $user->id]);
I want to share the approach i've taken for insert many posts to many users:`
factory(App\User::class, 50)->create()
->each(
function ($u) {
factory(App\Post::class, 10)->create()
->each(
function($p) use (&$u) {
$u->posts()->save($p)->make();
}
);
}
);
`
This workaround worked for me after being all day long looking for a way to seed the relationship
this worked for me in laravel v8
for ($i=0; $i<=2; $i++) {
$user = \App\Models\User::factory(1)->create()->first();
$product = \App\Models\Product::factory(1)->create(['user_id' => $user->id])->first();
}
I use a custom made relateOrCreate function that finds a random entry of that model in the database. If none exist, it creates a new one:
function relateOrCreate($class) {
$instances = $class::all();
$instance;
if (count($instances) > 0) {
$randomIndex = rand(0, (count($instances) - 1));
$instance = $instances[$randomIndex];
}
else {
$instance = $class::factory()->create();
}
return $instance;
}
Then I use it like so:
$relatedUser = relateOrCreate(User::class);
return [
'user_id' => $relatedUser->id,
// ...
];