PHP Notice: Array to string conversion; Database Factories - php

I have a talent model which can have many educations which I want to populate using data factories. But populating using artisan tinker the education data cause "Array to string conversion". From what I see, I am not giving an array to be converted into a string. Below are the Education's model, migration & factory
Error Message
PHP Notice: Array to string conversion in C:/Core/.../vendor/laravel/framework/src/Illuminate/Support/Str.php on line 360
Received On Running This Statement
$talents->each( function($talent) { factory(App\Education::class)->create(['talent_id' => $talent->id]); })
class Education extends Model
{
protected $fillable = [
'talent_id',
'institution',
'education_level',
'other_education_level',
'qualification_field',
'start_date_month',
'start_date_year',
'end_date_month',
'end_date_year',
];
public function talent() {
return $this->belongsTo(Talent::class);
}
}
Schema::create('educations', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('talent_id')->index();
$table->string('institution');
$table->string('education_level');
$table->string('other_education_level')->nullable();
$table->string('qualification_field');
$table->unsignedTinyInteger('start_date_month');
$table->unsignedSmallInteger('start_date_year');
$table->unsignedTinyInteger('end_date_month')->nullable();
$table->unsignedSmallInteger('end_date_year')->nullable();
$table->timestamps();
});
$factory->define(Education::class, function (Faker $faker) {
return [
'talent_id' => factory(\App\Talent::class),
'institution' => $faker->word,
'education_level' => $faker->word,
'other_education_level' => $faker->word,
'qualification_field' => $faker->words,
'start_date_month' => rand(1, 12),
'start_date_year' => rand(1970, 2000),
'end_date_month' => rand(1, 12),
'end_date_year' => rand(1970, 2000),
];
});
Below are the tinker commands I ran
$talents = App\Talent::all()
$talents->each( function($talent) { factory(App\Education::class)->create(['talent_id' => $talent->id]); })
The $talents->each( function($talent) { factory(App\Education::class)->create(['talent_id' => $talent->id]); }) is the cause, but I don't understand why
The same command with different class model works say for example
$talents->each( function($talent) { factory(App\WorkExperience::class)->create(['talent_id' => $talent->id]); })
class WorkExperience extends Model
{
protected $fillable = [
'talent_id',
'title',
'employment_type',
'company',
'start_date_month',
'start_date_year',
'end_date_month',
'end_date_year',
'description',
];
public function talent() {
return $this->belongsTo(Talent::class);
}
}
Schema::create('work_experiences', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('talent_id')->index();
$table->string('title');
$table->string('employment_type');
$table->string('company');
$table->unsignedTinyInteger('start_date_month');
$table->unsignedSmallInteger('start_date_year');
$table->unsignedTinyInteger('end_date_month')->nullable();
$table->unsignedSmallInteger('end_date_year')->nullable();
$table->text('description');
$table->timestamps();
});
$factory->define(WorkExperience::class, function (Faker $faker) {
return [
'talent_id' => factory(\App\Talent::class),
'title' => $faker->word,
'employment_type' => $faker->word(['Internship', 'Part Time', 'Full Time']),
'company' => $faker->word,
'start_date_month' => rand(1, 12),
'start_date_year' => rand(1970, 2000),
'end_date_month' => rand(1, 12),
'end_date_year' => rand(1970, 2000),
'description' => $faker->paragraph,
];
});

This question was initially posted from the Laracasts website forum and I've just received the solution.
The issue was in my EducationFactory:
'qualification_field' => $faker->words
$faker->words return an array of strings. The solution is to use $faker->sentence
'qualification_field' => $faker->sentence

Your issue probably lies in this piece of code:
'talent_id' => factory(\App\Talent::class),
The factory method will return an array of data for the Talent class, and will try to apply it to the talent_id , and fail.
To correct your problem, do:
'talent_id' => function () { return factory(\App\Talent::class)->create()->id; }

Related

Laravel 9 error Array to string conversion when run seeder

I have an error on Laravel 9 when run seeder, its say Array to string conversion
I have a same seeder type json before this DataMaster table, and its working. But when i run DataMasterSeeder, its not working
My seeder:
<?php
namespace Database\Seeders;
use App\Models\DataMaster;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DataMasterSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
//SDU
DataMaster::create(['formId' => 1, 'userId' => 1, 'kecamatanId' => 1, 'desaId' => null, 'fieldDatas' => [['id' => '1', 'name' => 'jumlah', 'title' => 'Jumlah', 'value' => '4605']], 'level' => 'kecamatan']);
}
}
And my DataMaster migration:
public function up()
{
Schema::create('data_masters', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('formId');
$table->unsignedBigInteger('userId');
$table->unsignedBigInteger('kecamatanId')->nullable();
$table->unsignedBigInteger('desaId')->nullable();
$table->json('fieldDatas');
$table->enum('level', ['kecamatan', 'desa']);
$table->timestamps();
$table->foreign("formId")->references("id")->on("forms")->onDelete('cascade');
$table->foreign("userId")->references("id")->on("users")->onDelete('cascade');
$table->foreign("kecamatanId")->references("id")->on("kecamatans")->onDelete('cascade');
$table->foreign("desaId")->references("id")->on("desas")->onDelete('cascade');
});
}
I have another seeder like fieldDatas json field in this DataMaster seeder, and i run it successfully before run DataMaster seeder.
you should encode the field fieldDatas before inserting
DataMaster::create([
'formId' => 1,
'userId' => 1,
'kecamatanId' => 1,
'desaId' => null,
// here...
'fieldDatas' => json_encode([['id' => '1', 'name' => 'jumlah', 'title' => 'Jumlah', 'value' => '4605']]),
'level' => 'kecamatan',
]);

Laravel how can i store an API parent data with some array child data?

So i want to store Parent data and array child data at the same time in laravel 9 rest api, but i dont even know how to format the array store, i was tried some looping code and still cant. the relationship was one parent has many child. i want to store like this
this what i expect store value
the parent model it just have name & class, and the child model was foreign parent_id & name. the model
here my controller
public function store(Request $request){
$this->validate($request, [
'parent_name' => 'required',
'class' => 'required',
'child.*' => 'array',
]);
$child = $request->child;
$chd = [];
foreach($child as $cd){
array_push($chd, Child::create([
'child' => $cd,
]));
}
$this->updates->create($request->all());
return $this->index();
}
here the migration :
Schema::create('parent', function (Blueprint $table) {
$table->increments('id');
$table->string('parent_name');
$table->string('class');
$table->timestamps();
});
Schema::create('child', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->integer('parent_id')->unsigned()->index();
$table->foreign('parent_id')->references('id')->on('parent_id')->onDelete('cascade');
$table->timestamps();
});
}
You may want to try this
public function store(Request $request) {
$rules = [
'parent_name' => 'required|string|max:255',
'class' => 'required|string|max:255',
'child' => 'required|array',
'child.name' => 'required|string|max:255',
];
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
return response()->json(['error' => true, 'error_msg' => 'invalid input']);
}
DB::beginTransaction();
$parent = Parent::create([
'parent_name' => $request->parent_name,
'class' => $request->class,
]);
foreach ($request->child AS $child) {
$child = Child::create([
'parent_id' => $parent->id,
'name' => $child['name'],
]);
}
DB::commit();
return response()->json(['error' => false]);
}

Laravel Quiz Storing The correct answers

I am very new to the Laravel, so I have a project for making a quiz. Currently I achieved to store my questions with radio-answers in the database, but I do not know how to:
1) Display all the questions with answers on web page.
2) Store the points for the user on each correct answer.
UPDATE: Thanks to ettdro I have solved my 1st problem. Only my 2nd left.
I would appreciate any help.
My Answer.php Model is empty for now. My Question.php Model:
class Question extends Model
{
// connect the models by adding a relationship to the Question model
public function answers()
{
return $this->hasMany(Answer::class);
}
}
My up function in Migration for Questions is:
public function up()
{
Schema::create('questions', function (Blueprint $table) {
$table->id();
$table->string('text');
$table->integer('points')->unsigned();
$table->timestamps();
});
}
My up function in Migration for Answers is:
public function up()
{
Schema::create('answers', function (Blueprint $table) {
$table->id();
// since answer is connected to the question
$table->integer('question_id');
$table->string('text');
$table->boolean('correct_one');
$table->timestamps();
});
}
My QuestionAnswerSeeder.php is:
// for filling the tables
class QuestionAnswerSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
// truncating the tables and then store each question and its answers.
public function run()
{
Question::truncate();
Answer::truncate();
$questionAndAnswers = $this->getData();
$questionAndAnswers->each(function ($question) {
$createdQuestion = Question::create([
'text' => $question['question'],
'points' => $question['points'],
]);
collect($question['answers'])->each(function ($answer) use ($createdQuestion) {
Answer::create([
'question_id' => $createdQuestion->id,
'text' => $answer['text'],
'correct_one' => $answer['correct_one'],
]);
});
});
}
// for the actual data, I use a separate getData method to keep it cleaner
// in this method, I return a big collection with all the questions and answers
private function getData()
{
return collect([
[
'question' => 'When did the World War 2 end?',
'points' => '1',
'answers' => [
['text' => '1939', 'correct_one' => false],
['text' => '1941', 'correct_one' => false],
['text' => '1945', 'correct_one' => true],
],
],
[
'question' => 'Who discovered America?',
'points' => '1',
'answers' => [
['text' => 'Adolf Hitler', 'correct_one' => false],
['text' => 'Napoleon Bonaparte', 'correct_one' => false],
['text' => 'Christopher Columbus', 'correct_one' => true],
],
],
]);
}
}
You should have a QuestionController.php that has this content:
/**
* In this function, you need to get all the data you want to pass to your view
* and send it to the compact function in the return statement.
*/
public function index() {
// This will return a collection of questions including their answers.
$questionsCollection = Question::all();
return view('myquestionsview', compact('questionsCollection'));
}
Next, because you returned myquestionsview in the index function, you will need a file named: myquestionsview.blade.php in the views folder.
To display the informations of your questions, in your myquestionsview.blade.php you should have something like
#foreach ($questions as $question)
{{ $question->text }}
// Now, we want to display each answers of the question.
#foreach ($question->answers as $answer)
{{ $answer->text }}
#endforeach
#endforeach
This is basically what you want to do for your first question.

Laravel API Resource not returning correct value with condition

I am new to Laravel API, I want to return a book where recommended === 1
In my resources, I have this
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'about' => $this->about,
'content' => $this->content,
// 'image' => asset('/storage/'.$this->image),
'image' => $this->image_url,
// 'recommended' => $this->recommended,
'recommended' => $this->when($this->recommended === 1, $this->recommended),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'author' => $this->author,
];
I want to return books when recommended === 1
My table is Like this
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->text('about');
$table->string('image');
$table->string('image_url');
$table->string('epub_url');
$table->integer('author_id');
$table->string('publisher');
$table->year('year');
$table->boolean('recommended')->default(0);
$table->timestamps();
});
I was able to achieve the same thing on web using this
public function index()
{
$data = array();
$data['recommends'] = Book::where('recommended', 1)->take(10)->get();
$data['latests'] = Book::orderBy('created_at', 'desc')->take(10)->get();
return view('welcome', compact("data"));
}
But I don't know how to replicate the same using Laravel API.
UPDATE
I was able to achieve the same thing on web using this
public function index()
{
$data = array();
$data['recommends'] = Book::where('recommended', 1)->take(10)->get();
$data['latests'] = Book::orderBy('created_at', 'desc')->take(10)->get();
return view('welcome', compact("data"));
}
But I don't know how to replicate the same using Laravel API.
Normally I will get all Books or Post like this using API Resource
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'about' => $this->about,
'content' => $this->content,
'image' => $this->image_url,
'recommended' => $this->recommended,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'author' => $this->author,
];
and call it like this in my controller
public function indexapi()
{
return BookResource::collection(Book::with('author')->Paginate(16));
}
But there some cases recommended is == 1 and some recommended == 0, in this case, I want to return data only when recommended == 1
I know my question is quite confusing
Thanks.
Thanks.
If I get it right, you want to filter & get only the books with ( recommended attribute == 1 ). If thats the case you shouldn't do it in your Collection file. You should do this filtering process in your Controller before passing any data to Collection.
Here is some code example from one of my project.
In ProductController.php FILE
public function index()
{
return new ProductCollection( Product::where('recommended','1')->get() );
}
As you can see , I'm filtering the products to get only the recommended ones. Then I'm sending this filtered data to the ProductCollection. This way The collection will only return the data I want.
In ProductCollection.php FILE
public function toArray($request)
{
return [
'data' => $this->collection->map( function($data) {
return [
'id' => (integer) $data->id,
'name' => $data->name,
'category_id' => $data->category_id,
'brand_id' => $data->brand_id,
'photos' => json_decode($data->photos),
'gtin' => $data->gtin
];
})
];
}
I don't have to make any changes in Collection. Because in this way , Collection should do the job for every data it gets.

Laravel - adding relationships to a factory-created model

I am testing an eager loading relationship which contains many to many relations. Right now I have the queries and attachments within the test. I'm wondering if there is a way to move them into the factory, rather than including it as part of your test. This would limit the size of the test and then these relations could be created and used every time a film factory is created.
test
public function grabFilmTest()
{
$film = factory(Film::class)->create();
$categories = Category::where('main-cat', 'Science')->where('sub-cat', 'Fiction')->first();
$languages = Languages::where('name', 'english')->first();
$film->categories()->attach($categories->id);
$film->languages()->attach($languages->id);
$response = $this->json('GET', '/film/' . $film->id)
->assertStatus(200);
$response
->assertExactJson([
'id' => $film->id,
'name' => $film->name,
'description' => $film->description,
'categories' => $film->categories->toArray(),
'languages' => $film->languages->toArray()
}
filmFactory
$factory->define(\App\Models\Film::class, function (Faker $faker){
return [
'id' => $faker->uuid,
'name' => $faker->text,
'description' => $faker->paragraph,
];
});
If anyone could help with how i could do this or an example it would be great :D
You could use factory states and factory callbacks.
$factory->define(\App\Models\Film::class, function (Faker $faker){
return [
'id' => $faker->uuid,
'name' => $faker->text,
'description' => $faker->paragraph,
];
});
$factory->define(\App\Models\Category::class, function (Faker $faker){
return [
// Category fields
];
});
$factory->define(\App\Models\Language::class, function (Faker $faker){
return [
// Language fields
];
});
$factory->afterCreatingState(\App\Models\Film::class, 'with-category', function (\App\Models\Film $film) {
$category = factory(\App\Models\Category::class)->create();
$film->categories()->attach($category->id);
});
$factory->afterCreatingState(\App\Models\Film::class, 'with-language', function (\App\Models\Film $film) {
$language = factory(\App\Models\Language::class)->create();
$film->categories()->attach($language->id);
});
Then you can use in tests like this:
public function grabFilmTest()
{
$film = factory(Film::class)->create();
$filmWithCategory = factory(Film::class)->state('with-category')->create();
$filmWithLanguage = factory(Film::class)->state('with-language')->create();
$filmWithCategoryAnLanguage = factory(Film::class)->states(['with-category', 'with-language'])->create();
// ...
}
PS: I don't recommend using existing data. From experience, I can tell you that can become really painful.
You can use factory callbacks to do it in the factory file:
<?php
use \App\Models\Film;
use \App\Models\Category;
use \App\Models\Languages;
$factory->define(Film::class, function(Faker $faker){
return [
'id' => $faker->uuid,
'name' => $faker->text,
'description' => $faker->paragraph,
];
});
$factory->afterCreating(Film::class, function(Film $film, Faker $faker) {
$category = Category::where('main-cat', 'Science')->where('sub-cat', 'Fiction')->first();
$language = Languages::where('name', 'english')->first();
$film->categories()->attach($category);
$film->languages()->attach($language);
});

Categories