How can I implement laravel migration test before laravel migrate? - php

While writing unit tests for my migrations, I noticed that it does not check if the table it is depends on for columns with foreign keys exists. Then i decided to add a new function to my test case for cheched the table which is the depends on my new migration. New function like this;
public function test_users_table_has_foreign_keys()
{
$this->assertTrue(Schema::hasTable('workareas'));
}
But these assertions do not work parallel to migration sequence. Therefore, when I migrate, I cannot determine whether it will give an error or not with the test process. Testcase does not fail even if migration sequence is wrong.
Before adding the 'public function test_users_table_has_foreign_keys' function, my test scenario was as follows;
<?php
namespace Modules\SYS\Tests\Unit;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Schema;
use Modules\SYS\Entities\User;
use Illuminate\Support\Str;
class UsersTest extends TestCase
{
use RefreshDatabase;
private $columns = [
'id',
'name',
'email',
'email_verified_at',
'password',
'type',
'remember_token',
'created_at',
'updated_at'
];
public function test_users_database_has_expected_columns()
{
$this->assertTrue(
Schema::hasColumns('users', $this->columns)
);
}
public function test_users_table_columns_matched()
{
$this->assertTrue(sizeof(array_diff(Schema::getColumnListing('users'), $this->columns)) == 0);
}
public function test_user_has_been_created()
{
$this->create_user();
$this->assertDatabaseCount('users', 1);
}
public function test_user_has_been_updated()
{
$user = $this->create_user();
$user->name = 'Updated Name';
$user->save();
$this->assertTrue($user->name == 'Updated Name');
}
public function test_user_has_been_deleted()
{
$user = $this->create_user();
$user->delete();
$this->assertDatabaseCount('users', 0);
}
private function create_user()
{
return
User::create([
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
'type' => 'admin'
]);
}
}
i have a lot of tables and relations so i need to detect if i am making mistakes with testcases.
What kind of test scenario should I prepare to determine the accuracy of the migration sequence?

Related

Laravel - Eloquent attach() not working for models

TL;DR
Trying to get three models to interact using eloquent for a rest api.
User - belongsToMany(pulls)
Pull - belongsToMany(user) && belongsToMany(boxes)
Box - belongsToMany(pulls)
The pull_user table is working perfectly, I can just attach a user after I save a pull. Saving a box works fine but the attach doesn't work/enter anything into the pivot table (I get no errors though).
The Problem
I can't get a pivot table that associates two of my models together to attach() after a save. I have the three models listed above, the pivot is working for pull_user but not for pull_box even though the save for box is working perfectly. I am able to save a box without an error but the association just never occurs (no error).
The Code
pull_box.php
class PullBox extends Migration
{
public function up()
{
Schema::create('pull_box', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('pull_id');
$table->integer('box_id');
});
}
public function down()
{
Schema::dropIfExists('pull_box');
}
}
Pull.php
class Pull extends Model
{
protected $fillable = ['from', 'to', 'runit_id', 'start_time', 'end_time', 'box_count', 'pull_status', 'audit_status', 'status', 'total_quantity', 'accuracy'];
public function users(){
return $this->belongsToMany('App\User');
}
public function boxes(){
return $this->belongsToMany('App\Box');
}
}
Box.php
class Box extends Model
{
protected $fillable = ['user_id','from', 'to', 'runit_id', 'start_time', 'end_time', 'pull_id', 'total_quantity', 'status', 'accuracy'];
public function pulls(){
return $this->belongsToMany('App\Pull');
}
}
BoxController.php
public function store(Request $request)
{
$this->validate($request, [
'user_id' => 'required|integer',
...
]);
$user_id = $request->input('user_id');
...
$box = new Box([
'user_id' => $user_id,
...
]);
$pull = Pull::whereId($pull_id)->first();
if($box->save()){
$pull->boxes()->attach($box->id);
$box->view_box = [
'href' => 'api/v1/box/' . $box->id,
'method' => 'GET'
];
$message = [
'msg' => 'Box created',
'box' => $box,
'pull' => $pull_id
];
return response()->json($message, 201);
}
$response = [
'msg' => 'Box creation error, contact supervisor',
];
return response()->json($response, 404);
}
The Solution
I need to know how I can get this association working. I am going to need to add a new layer in under the pull for Item, but I don't want to move one before I solve this. I think that my problem has to stem from a syntactical/logical error on my part but I can't see it. There are a bunch of questions on SO that are very close to giving me a solution, but after reading them I wasn't able to solve my problem.
Any help is appreciated.
Try renaming your pull_box table to box_pull, pivot tables on laravel must be in alphabetical order. If you want to use custom name on pivot table you have to extends your pivot, for example:
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class PullBox extends Pivot
{
protected $table = 'pull_box';
}
And your many to many relationships:
class Pull extends Model
{
protected $fillable = ['from', 'to', 'runit_id', 'start_time', 'end_time', 'box_count', 'pull_status', 'audit_status', 'status', 'total_quantity', 'accuracy'];
public function users(){
return $this->belongsToMany('App\User');
}
public function boxes(){
return $this->belongsToMany('App\Box')->using('App\PullBox');
}
}
class Box extends Model
{
protected $fillable = ['user_id','from', 'to', 'runit_id', 'start_time', 'end_time', 'pull_id', 'total_quantity', 'status', 'accuracy'];
public function pulls(){
return $this->belongsToMany('App\Pull')->using('App\PullBox');
}
}

Laravel Seeding using Factory for one to one relation

I want to create a test data using laravel seed. I can seed for one table. Here is :
ModelFactory.php:
/** #var \Illuminate\Database\Eloquent\Factory $factory */
$factory->define(App\User::class, function (Faker\Generator $faker) {
static $password;
return [
'email' => $faker->unique()->safeEmail,
'password' => $password ?: $password = bcrypt('secret'),
'remember_token' => str_random(10),
'type' => '0'
];
});
UsersTableSeeder.php :
<?php
use Illuminate\Database\Seeder;
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run() {
factory(App\User::class, 50)->create()->each(function ($u) {
$u->posts()->save(factory(App\Post::class)->make());
});
}
}
I have two tables which are users and user_information. They have one-to-one relation. I want to seed user_information with users. But i have no idea, how i can do...
My models relation functions :
User.php
public function userInformation() {
return $this->hasOne('App\UserInformation', 'user_id', 'id');
}
UserInformation.php
public function users() {
return $this->belongsTo('App\User', 'user_id', 'id');
}
Laravel version is 5.3
I followed these docs :
https://laravel.com/docs/5.3/seeding
https://laravel.com/docs/5.3/database-testing#writing-factories
Answering because this came up in a Google search and might help others:
AFAIK the model hasn't got anything to do with seeding, that's used for query building and constraints.
What can do is create a user, every time you call on your factory that requires the user_id.
Credit to prodigy10 for this answer.
$factory->define(User_Information::class, function ($faker) use ($factory) {
return [
'name' => $faker->company(),
'user_id' => factory(User::class)->create()->id
];
});
For one-to-one relationships, its good practice to make the relationship methods singular
User.php
public function userInformation()
{
return $this->hasOne('App\UserInformation', 'user_id', 'id');
}
UserInformation.php
public function user()
{
return $this->belongsTo('App\User', 'user_id', 'id');
}
Above solution for Laravel 8, userInformation factory will have :
'user_id' => User::factory()->create()->id,
if you need to create userInformation table only when there's a corresponding user, the above solution will seed new user_ids into the userinformation table and you may end up with more userinformation rows than users. To fix this, you need to seed only UserInformation Factory seeder in DatabaseSeeder run method and not UserFactory. it will automatically seed the users table.
Alternatively, if you have the users table already defined and wants to seed userInformation table for your number of users.
For Laravel 8, its is shown below:
public function definition()
{
$users = User::all();
return [
'name' => $this->faker->company,
'user_id' => $this->faker->unique()->numberBetween(1, $users->count())
];
}
Since its a one to one relationship. You can generate the list of users and use unique and numberBetween functions to fill unique user_ids.
This works in Laravel 9. Amend your DatabaseSeeder.php such as:
public function run()
{
User::factory(10)->create()->each(function ($user) {
$phone = Phone::factory()->make();
$user->phone()->save($phone); // phone() is hasOne ralationship in User.php
});
}
You don't need to define user_id column in PhoneFactory.php. The Laravel automatically saves corresponding user_id in the database.

How to auto fill foreign key column value in Laravel 5.2?

I'm working on laravel 5.2 and also beginner of laravel. I have users and clients table in the database. user field of clients table stores the value of id column(primary key) of users table. i.e, one user have many clients and one client belongs to one user. Now come to problem. When I'm going to insert new client of the logged in user, I'm getting error. No any error message. I searched and come to know that perhaps it happens because of foreign key column name is user, not user_id. So I updated my Client model from return $this->belongsTo('App\User'); to return $this->belongsTo('App\User', 'user');. But still failed. If any one knows the answer, answer will be appreciated. Here is my code.
ClientController (Try 1)
$request->user()->clients()->create([
'user' => $request->user()->id,
'email' => $request->email,
'age' => $request->age
]);
ClientController (Try 2)
$request->user()->clients()->create([
'email' => $request->email,
'age' => $request->age
]);
Client Model
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\Ak\Scopes\AgeScope;
class Client extends Model
{
protected static function boot()
{
parent::boot();
static::addGlobalScope(new AgeScope);
}
public function user()
{
return $this->belongsTo('App\User', 'user');
}
}
User Model
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
protected $fillable = [
'name', 'email', 'password', 'country'
];
protected $hidden = [
'password', 'remember_token',
];
protected $date = ['deleted_at'];
public function client()
{
return $this->hasMany('App\Client', 'user');
}
}
sorry your relationship is wrong. and moreover you are using
$request->user()->clients()
instead of
$request->user()->client()
the client in your relationship is singular. And the client function body should be
return $this->hasMany('App\Client', 'user');
and in your client model add
$fillable = ['email', 'age', 'user'];
and change the user function in your client model to
public function user()
{
return $this->belongsTo('App\User', 'user');
}
the orm should be like this
$request->user()->client()->create([
'user' => $request->user()->id,
'email' => $request->email,
'age' => $request->age
]);
Try adding protected $fillable = ['email', 'age']; to your Client model.
More on mass assignement
Edit:
try this
Routes:
Route::post('user/{user}/client', 'YourController#store');
Controller:
public function store(Request $request, User, $user){
$user->client()->create([
'user' => $request->user()->id,
'email' => $request->email,
'age' => $request->age
]);
}
Problem is Solved
Problem is solved as I have to write public $timestamps = false; in Client Mode. After it, I don't need to insert user value manually. Because laravel is doing that task itself as I have write foreign key in User and Client Models relationship. i.e, return $this->hasMany('App\Client', 'user') and return $this->belongsTo('App\User', 'user') manually.
I can't understand that what should I do for this feature of laravel. Should I cry or laugh?

laravel 5 multi form data save in database

I use two different forms and connected to database it's working fine. But when I do an insert values stored as two different rows in database.
When do we have to use seeder? Is what I wrote coding proper laravel 5?
Controller file
<?php namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Http\Requests\ListFormRequest;
use App\Http\Requests\LoginFormRequest;
use Illuminate\Support\Facades\Input;
use Illuminate\Http\Request;
use Response;
use App\Test;
class testController extends Controller {
public function test()
{
return view('test',array('title' => 'test'));
}
public function lockTest(ListFormRequest $test)
{ $user = new Test();
$user->firstname = Input::get('firstname');
$user->password = Input::get('password');
$user->email = Input::get('email');
$user->save();
return view('login');
}
public function login()
{
return view('login',array('title' => 'login'));
}
public function userLogin(LoginFormRequest $test1)
{
$user = new Test();
$user->lastname = Input::get('lastname');
$user->middlename = Input::get('middlename');
$user->save();
return Response::make('Sucessfully Registered!');
}
}
Route file
<?php
Route::get('/', 'testController#test');
Route::post('login', 'testController#lockTest');
Route::get('login', 'testController#login');
Route::post('userLogin', 'testController#userLogin');
Route::controllers([
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]);
Seeder file
<?php
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
use App\Test;
class TestsSeeder extends Seeder {
public function run() {
DB::table('users')->truncate();
$qwerty = Input::all();
foreach($qwerty as $qwertySingle) {
Test::create([
'firstname' => $qwertySingle->firstname,
'password' => $qwertySingle->password,
'email' => $qwertySingle->email,
'lastname' => $qwertySingle->lastname,
'middlename' => $qwertySingle->middlename,
]);
}
DB::table('tests')->insert($qwerty);
}
}
Model file
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
use App\DB;
class Test extends Model {
protected $guarded = array();
protected $table = 'tests';
protected $fillable = ['firstname', 'password', 'email', 'lastname', 'middlename'];
}
The seeder is used when you want to populate your database with fake data, so you can test the waters of your app with thousands of dummy records.
The run method from your seeder should be something like the following, to give you some ideas:
Test::truncate();
Test::unguard();
$password = Hash::make('secret');
foreach (range(1, mt_rand(10, 20)) as $index) {
Test::create([
'firstname' => "first name {$index}",
'password' => $password,
'email' => "myemail{$index}#mydomain.com",
'lastname' => "last name {$index}",
'middlename' => "middlename {$index}",
]);
}

How to run more logic after user registration in Laravel 5

I am using the default user registrar in Laravel 5. When the user registers there is some information stored in the session so after the user has been created I want to run some code to process the session data, store it and link it to the user. Would I just amend the create method in the registrar from this:
public function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password'])
]);
}
to this:
public function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password'])
]);
// do my other logic to process session data here
return $user;
}
Or is there a better way to do it?
You can use a model event, place this code in your model.
public static function boot()
{
static::created(function($model)
{
//do other logic here
});
}
http://laravel.com/docs/5.0/eloquent#model-events
You can also opt for a model observer:
<?php namespace App\Observers;
use Illuminate\Database\Eloquent\Model as Eloquent;
class UserObserver {
public function created(Eloquent $model)
{
//do other logic
}
}
You'll need a listener to this observer:
\App\User::observe(new \App\Observers\UserObserver);
You can place the listener in the routes.php file to test it.
Later on you can move the listener to a more appropriate location such as a ServiceProvider.
http://laravel.com/docs/5.0/eloquent#model-observers

Categories