I have a database with two columns, brands and shops. Each brand can owe several shops, and I want to seed my database via Fakers using Laravel.
So after setting up the migrations and the relationships in the models
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Brand extends Model
{
/**
* Get the shops for the brand.
*/
public function shops()
{
return $this->hasMany('App\Shop','sh_brand_id');
}
}
And:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Shop extends Model
{
public function user() {
return $this->belongsTo('App\Brand','sh_brand_id');
}
}
I want to use a Factory to seed the database.
<?php
use Faker\Generator as Faker;
$factory->define(App\Shop::class, function (Faker $faker) {
return [
'name' => $faker->company,
'address' => $faker->address,
];
});
And
use Faker\Generator as Faker;
$factory->define(App\Brand::class, function (Faker $faker) {
return [
'name' => $faker->company,
'logo_url' => $faker->imageUrl(640, 480),
'website' => $faker->url,
'description' => $faker->text(500),
'telephone_number' =>'31'. $faker->randomNumber(8),
'principal_address' => $faker->address,
'email' => $faker->unique()->safeEmail,
];
});
And finally I need to seed the database using those Factories. There are documentation in the website and many examples for do it, but each solution I've found let me generate only one shop for each brand, and I want to generate many shops for each brands.
What is the best way to do this?
Put it directly in your factory. I use a helper method getInstanceOf to pick a random instance of another model.
use Faker\Generator as Faker;
use App\Brand;
use App\Shop;
function getInstanceOf($class, $returnIdOnly = true) {
$instance = $class::inRandomOrder()->first() ?? factory($class)->create();
return $returnIdOnly ? $instance->id : $instance;
}
$factory->define(Shop::class, function (Faker $faker) {
return [
'name' => $faker->company,
'address' => $faker->address,
'sh_brand_id' => getInstanceOf(Brand::class)
];
});
Then when seeding,
factory(App\Brand::class, 10);
factory(App\Shop::class, 50);
I've found this workaround that works for me:
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder {
/**
* Run the database seeds.
*
* #return void
*/
public function run() {
factory(App\Brand::class, 50)->create()
->each(
function ($br) {
factory(App\Shop::class, 10)->create()
->each(
function($sh) use (&$br) {
$br->shops()->save($sh)->make();
}
);
}
);
}
}
Related
I'm using the laravel-excel package and want to import excel into an array, it works well for the main code (without validation), but when I tried to add validation/rule, the validation not working properly (I mean, like the validation is just skipped, so if I upload a file that doesn't fit the format, the array output still comes out), here's my import code
<?php
namespace App\Imports;
use App\Models\Request;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithValidation;
use Illuminate\Validation\Rule;
class RequestImport implements ToModel, WithValidation, WithHeadingRow
{
use Importable;
/**
* #param array $row
*
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
return new Request([
'no' => $row[0],
'username' => $row[1],
'name' => $row[2],
]);
}
// create validation by heading row
public function rules(): array
{
return [
'no' => 'required',
'username' => 'required',
'name' => 'required',
];
}
}
and here's my controller code when I call the RequestImport class
$file = request()->file('fileExcel');
// save file
if (isset($request->upload)) {
$this->saveFile($file);
}
// return excel import into array
$data = (new RequestImport)->toArray($file); //here I call the RequestImport
return response()->json([
'status' => 'success',
'message' => 'success,
'data' => $data[0],
]);
Is something wrong with my code? or I missed something?
it's not official but I found a workaround.
first, I added importable concern,
then defined a class attribute $data.
this will store rows once all validations are satisfied.
<?php
namespace App\Imports;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\ToArray;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithValidation;
class ImportData implements ToArray, WithValidation, WithHeadingRow
{
use Importable;
public $data;
public function array(array $rows)
{
$this->data = $rows;
}
public function rules(): array
{
return [
// validation rules
];
}
}
in my controller
$import = new ImportData;
Excel::import($import, request()->file('file'));
return response()->json($import->data);
I'm trying to expand the User Model with another Table (profile) to get a profile-picture, position, etc.
Can I override the index() function of the User Model to do that?
Current Model-Code:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
protected $fillable = [
'name',
'email',
'password',
'user_group'
];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
];
}
What you are trying to do is setting up a relationship between the User Model and a new Profile Model. To do this you first need to create a Model Profile and it's associated Tabble profiles
php artisan make:model Profile --migration
In database\migrations there should be a file called something like that 2022_11_28_223831_create_profiles_table.php
Now you need to add a foreign key which indicates to which User this profile belongs.
public function up()
{
Schema::create('profiles', function (Blueprint $table) {
$table->id();
// $table->string('path_to_picture')
// user id
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
}
Now in your User Model add the following function
public function profile()
{
return $this->hasOne(Profile::class);
}
And in your Profile Model
public function user()
{
return $this->belongsTo(User::class);
}
Run php artisan migrate and everything should work as expected
If you want to test if the relationship works as expected create a new TestCase with
php artisan make:test ProfileUserRelationTest
In tests\Feature\ProfileUserRelationTest.php
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
use App\Models\User;
use App\Models\Profile;
use Illuminate\Support\Facades\Hash;
class ProfileUserRelationTest extends TestCase
{
use RefreshDatabase;
public function test_the_relation_between_user_and_profile_works()
{
$user = User::create([
'name' => 'John Doe',
'email' => 'jd#example.com',
'password' => Hash::make('password'),
]);
$profile = new Profile();
$profile->user_id = $user->id;
$profile->save();
$this->assertEquals($user->id, $profile->user->id);
$this->assertEquals($user->name, $profile->user->name);
$this->assertEquals($profile->id, $user->profile->id);
}
}
Now you can run php artisan test to see if everything works.
Be carefull this will refresh your database! So don't test in production.
Output should something like this
PASS Tests\Unit\ExampleTest
✓ that true is true
PASS Tests\Feature\ExampleTest
✓ the application returns a successful response
PASS Tests\Feature\ProfileUserRelationTest
✓ the relation between user and profile works
Tests: 3 passed
Time: 0.35s
Learn more about Relationships in Laravel: https://laravel.com/docs/9.x/eloquent-relationships
Learn more about migrations: https://laravel.com/docs/9.x/migrations
Alternative
$user = User::create([
'name' => 'John Doe',
'email' => 'jd#example.com',
'password' => Hash::make('password'),
]);
$user->profile()->create(...); // replace the ... with the things you want to insert you dont need to add the user_id since it will automatically added it. It will still work like the one above.
I'm work on Restful API , I have exception message when i want to make seeding fake data in database.
I make database fresh .
php artisan migrate:fresh
I seeding on database .
php artisan db:seed
I make model with migration and controller:
Migration: 000000_create_posts_table.php
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('title');
$table->text('content');
$table->dateTime('date_written');
$table->String('feature_image')->unllable();
$table->integer('votes_up')->unllable();
$table->integer('votes_down')->unllable();
// TelationShipe.
$table->integer('user_id');
$table->integer('category_id');
$table->timestamps();
});
Model: Post.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = [
'title' , 'content' , 'date_written' ,
'feature_image' , 'votes_up' ,
'votes_down' , 'user_id' ,
'category_id'
];
}
Controller: PostController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PostController extends Controller
{
//
}
Seeder: DatabaseSeeder.php
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* #return void
*/
public function run()
{
factory(\App\Post::class , 100 )->create();
}
}
Factory: PostFactory.php
<?php
/** #var \Illuminate\Database\Eloquent\Factory $factory */
use App\User;
use Faker\Generator as Faker;
use Illuminate\Support\Str;
/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/
$factory->define(User::class, function (Faker $faker) {
return [
'title' => $faker->title,
'content' => $faker->text(400),
'date_written' => $faker->new(),
'feature_image' => $faker->imageUrl(),
'votes_up' => $faker->numberBetween(1 , 100),
'votes_down' => $faker->numberBetween(1 , 100),
'user_id' => $faker->numberBetween(1 , 15),
'category_id' => $faker->numberBetween(1 , 15),
];
});
but the actual output console :
InvalidArgumentException : Unknown formatter "new" at
~/vendor/fzaninotto/faker/src/Faker/Generator.php:242
238|
239| return $this->formatters[$formatter];
240| }
241| }
242| throw new \InvalidArgumentException(sprintf('Unknown formatter "%s"', $formatter));
243| }
244|
245| /**
* Replaces tokens ('{{ tokenName }}') with the result
* from the token method call
*/
Exception trace:
1 Faker\Generator::getFormatter("new")
~/vendor/fzaninotto/faker/src/Faker/Generator.php:222
2 Faker\Generator::format("new", [])
~/vendor/fzaninotto/faker/src/Faker/Generator.php:279
Please use the argument -v to see more details.
Change this line
From
'date_written' => $faker->new(),
To
'date_written' => now(),
now() will return a Carbon instance of the current time which your database migration require
There's no such function on faker generator called new
Hope this helps
I am using model factories in NewsTableSeeder, but I get this error when I entered db:seed.
I want to know why I can't use create() in my seeder.
Here is my News model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class News extends Model
{
protected $table = 'news';
protected $primaryKey = 'id';
public function home_news_lists() {
return $this->select('id', 'news_title', 'news_update')
->orderBy('news_update', 'DESC')
->limit(5)
->get();
}
public function lists() {
return News::all();
}
}
Model Factories:
$factory->define(App\Models\News::class, function (Faker\Generator $faker)
{
static $password;
$faker = $faker->create('zh_TW');
return [
'news_title' => $faker->sentence(),
'news_content' => $faker->paragraph(),
'news_author' => $faker->name(),
'news_pageviews' => $faker->numberBetween(1, 100),
'news_file' => ' ',
'news_img' => $faker->imageUrl($width, $height, 'business'),
'created_at' => $faker->dateTimeBetween('2012', 'now', 'zh_TW'),
'updated_at' => $faker->dateTimeBetween('2015', 'now', 'zh_TW')
];
});
NewsTableSeeder :
<?php
use Illuminate\Database\Seeder;
class NewsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
factory(App\Models\News::class, 50)->create();
}
}
I can't tell 100% without seeing exactly the error you got, but I do believe there is no create() method on the $faker object.
I believe what you mean to do is:
$factory->define(App\Models\News::class, function (Faker\Generator $faker)
{
static $password;
$faker = \Faker\Factory::create('zh_TW'); // change to this
return [
...
];
}
I would just create a new faker generator (\Faker\Generator) that gets returned from calling \Faker\Factory::create($locale) and use that instead. Otherwise, I believe your next best option is to override wherever Laravel instantiates the \Faker\Generator $faker object that gets passed into the callback, but that may get hacky if Laravel doesn't provide a clean way to do it.
The create() method is a static call on the \Faker\Factory method. It accepts a locale as the parameter and uses en_US as the default locale.
$faker = $faker->create('zh_TW');
The error message said this code is wrong.
What is your purpose to use this code?
I am new in laravel5 Framework. when I insert data into database using laravel5 at that time I get error like....
FatalErrorException in ClientFormRequest.php line 10:
Cannot make static method Symfony\Component\HttpFoundation\Request::create() non static in class App\Http\Requests\ClientFormRequest
my all files are below...
app/Http/Controller/RegisterController.php
<?php namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Http\Requests\ClientFormRequest;
use Illuminate\Http\Request;
class RegisterController extends Controller {
public function create()
{
return view('Client.client');
}
public function store(ClientFormRequest $request)
{
return \Redirect::route('Client.client')
->with('message', 'Record Inserted!');
}
}
app/Http/Requests/ClientFormRequest.php
<?php namespace App\Http\Requests;
use Stringy\create;
use App\User;
use Validator;
use App\Http\Requests\ClientFormRequest;
class ClientFormRequest extends Request {
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
}
public function validator(array $data)
{
return Validator::make($data, [
'fullname' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
]);
}
public function create(array $data)
{
return client::create([
'fullname' => $data['fullname'],
'email' => $data['email'],
]);
}
}
Routes
Route::get('client', 'RegisterController#create');
Route::post('contact_store', 'RegisterController#store');
First of all, i would suggest you to watch Laravel 5 Fundamentals repeatedly since it is free. Other series also give great information.
Secondly, I would suggest you to use at least Sublime Text and some useful packages to be able to inspect the depth nested relations of system files (Namespaces, Interfaces, Inheritance Tree etc...). If you can't/might not, this friend will serve you anytime Laravel API
Third, AFAIK, Laravel Request is build onto the Symfony' Request Component. Since you are trying to overload one of its core function as non static, you are getting this error.
In addition, to be honest, i wouldn't put my user/client model creation logic into the requests. Laravel provides an good example for this kind of misconception. In the App\Services folder, you will find a registrar service for Laravel oem user model.
Let's inspect the problem with different cases.
but first, basic...
Lets assume that all logic should be put inside the controller.
RegisterController.php
<?php namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Request;
class RegisterController extends Controller {
public function create()
{
return view('Client.client');
}
public function store()
{
$data = Request::all(); //requested data via Facade
//prepare validatation
$validation = Validator::make($data, [
'fullname' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
]);
//validate
if ($validation->fails())
{
return redirect()->back()->withErrors($v->errors());
}
// create the client
Client::create([
'fullname' => Request::input('fullname'),
'email' => Request::input('email'),
]);
return \Redirect::route('Client.client')
->with('message', 'Record Inserted!');
}
}
Second Solution
You might be willing to separate the validation logic and apply some dependency injection.
RegisterController.php
<?php namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Http\Requests\ClientFormRequest;
class RegisterController extends Controller {
public function create()
{
return view('Client.client');
}
public function store(ClientFormRequest $request)
{
// create the client
Client::create([
'fullname' => $request->input('fullname'),
'email' => $request->input('email'),
]);
return \Redirect::route('Client.client')
->with('message', 'Record Inserted!');
}
}
ClientFormRequest.php
use Stringy\create;
use App\User;
use Validator;
use App\Http\Requests\ClientFormRequest;
class ClientFormRequest extends Request {
public function authorize()
{
return true;
}
public function rules()
{
return [
'fullname' => 'required|max:255',
'email' => 'required|email|max:255|unique:users'
];
}
}
Third Solution
You might be willing to take things further and even separate the object creation logic as an service to use it anywhere. Now your request file would stay the same. However,
RegisterController.php
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Http\Requests\ClientFormRequest;
use App\Services\ClientRegistrar;
class RegisterController extends Controller {
private $registrar;
public function __construct(ClientRegistrar $registrarService)
{
$this->registrar = $registrarService;
}
public function create()
{
return view('Client.client');
}
public function store(ClientFormRequest $request)
{
$newClient = $this->registrar->create($request->all());
return \Redirect::route('Client.client')
->with('message', 'Record Inserted!')->compact('newClient');
}
}
App\Services\ClientRegistrar.php
use App\Client;
use Validator;
use Illuminate\Contracts\Auth\Registrar as RegistrarContract;
class ClientRegistrar implements RegistrarContract {
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
public function validator(array $data)
{
return Validator::make($data, [
'fullname' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
]);
}
/**
* Create a new client instance after a valid registration.
*
* #param array $data
* #return Client
*/
public function create(array $data)
{
// create the client
return Client::create([
'fullname' => $data['fullname'],
'email' => $data['email'],
]);
}
}
To My Conclusion
There is no correct and best way to solve a problem. Stay with the best applicable and appropriate way for you and your project scale.
You also might be interested in;
Jeffrey Way's Laravel Auto Validate on Save
The error message tells you that you are overriding the create method in the ClientFormRequest class. So remove the method there. Instead create the new Client in your Controller.
Below I updated your classes to reflect the changes.
ClientFormRequest
class ClientFormRequest extends Request {
public function authorize()
{
return true;
}
public function rules()
{
}
public function validator(array $data)
{
return Validator::make($data, [
'fullname' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
]);
}
}
RegisterController
class RegisterController extends Controller {
public function create()
{
return view('Client.client');
}
public function store(ClientFormRequest $request)
{
// ClientFormRequest was valid
// create the client
Client::create([
'fullname' => $request->input('fullname'),
'email' => $request->input('email'),
]);
return Redirect::route('Client.client')
->with('message', 'Record Inserted!');
}
}