Laravel Eloquent Modal - First Row? - php

Inside od a Seeder File I tried to get the first record of my User-Model.
However I can't seem to find the correct way.
I want to fetch the first user, and get its id to use it in user_id (foreign key).
This is not working:
class TerminalsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
$user = User::findOrFail(1)->first();
DB::table('terminals')->delete();
Terminal::create([
'serial' => 'R-123-548-753',
'state' => 1,
'user_id' => $user->id
]);
}
}
It fails because I said "1", but it requires one parameter, my first row in user doesn't have an id of 1, but I don't want to hardcode this.

Simple solution is to use the firstOrFail
$user = User::firstOrFail(); //it will return the first record from the top
Terminal::create([
'serial' => 'R-123-548-753',
'state' => 1,
'user_id' => $user->id'
])

You have to check first user found or not
$user = User::first(); // returns the first record found in the database. If no matching model exist, it returns null
if($user)
Terminal::create([
'serial' => 'R-123-548-753',
'state' => 1,
'user_id' => $user->id
]);
}
If user found then it will create new record in terminal.

Related

Laravel exists custom validation rule unable to validate user id with phpunit

I have a validation rule taken from the Laravel Documentation which checks if the given ID belongs to the (Auth) user, however the test is failing as when I dump the session I can see the validation fails for the exists, I get the custom message I set.
I have dumped and died the factory in the test and the given factory does belong to the user so it should validate, but it isn't.
Controller Store Method
$ensureAuthOwnsAuthorId = Rule::exists('authors')->where(function ($query) {
return $query->where('user_id', Auth::id());
});
$request->validate([
'author_id' => ['required', $ensureAuthOwnsAuthorId],
],
[
'author_id.exists' => trans('The author you have selected does not belong to you.'),
]);
PHPUnit Test
/**
* #test
*/
function adding_a_valid_poem()
{
// $this->withoutExceptionHandling();
$user = User::factory()->create();
$response = $this->actingAs($user)->post(route('poems.store'), [
'title' => 'Title',
'author_id' => Author::factory()->create(['name' => 'Author', 'user_id' => $user->id])->id,
'poem' => 'Content',
'published_at' => null,
]);
tap(Poem::first(), function ($poem) use ($response, $user)
{
$response->assertStatus(302);
$response->assertRedirect(route('poems.show', $poem));
$this->assertTrue($poem->user->is($user));
$poem->publish();
$this->assertTrue($poem->isPublished());
$this->assertEquals('Title', $poem->title);
$this->assertEquals('Author', $poem->author->name);
$this->assertEquals('Content', $poem->poem);
});
}
Any assistance would be most appreciated, I'm scratching my head at this. My only guess is that the rule itself is wrong somehow. All values are added to the database so the models are fine.
Thank you so much!
In your Rule::exists(), you need to specify column otherwise laravel takes the field name as column name
Rule::exists('authors', 'id')
Since column was not specified, your code was basically doing
Rule::exists('authors', 'author_id')

laravel database seeder adding add foreign key id randomly to seeds

I am trying to create seeders for testing purposes. I have users that belongs to a room via a room id, these rooms are created via a room seeder, in my users seeder, I create a user and update the room_id attribute like this,
factory(App\User::class, 150)->create([
'host' => false,
'room_id' => App\Room::inRandomOrder()->first()->id
]);
My problem is that all users generated here, all get the same room id, how can a truly get a random room id from the database and use it in my seeder?
I had the same problem with seeding. The problem is that, you are overriding the factory's default model attributes by passing an array of "values". You are passing the value of App\Room::inRandomOrder()->first()->id to the create method. So you would have all users with the same room_id.
To solve this issue, in laravel 8, you can move the 'room_id' => Room::inRandomOrder()->first()->id to your UsersFactory definition:
class UsersFactory {
...
public function definition()
{
return [
'room_id' => Room::inRandomOrder()->first()->id
];
}
...
}
And create users like this,
App\User::factory()->count(150)->create([
'host' => false
]);
In older version of laravel, define your factory as below:
$factory->define(App\User::class, function ($faker) use ($factory) {
return [
'room_id' => Room::inRandomOrder()->first()->id
];
});
And create users like this,
factory(App\User::class, 150)->create([
'host' => false,
]);
Try:
App\Room::all()->random()->id
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
$users = factory(\App\User::class, 150)->create([
'host' => false,
'room_id' => $this->getRandomRoomId()
]);
}
private function getRandomRoomId() {
$room = \App\Room::inRandomOrder()->first();
return $room->id;
}
Try this one. It works for me. Hopefully it works for you.
Try this one. Also, make sure that you have multiple auto incremented room entries in the room table.
$factory->define(App\User::class, function ($faker) use ($factory) {
return [
'host' => false,
'room_id' => $factory->create(App\Room::class)->id
];
});

Laravel Factory: Manual Increment of Column

For the following factory definition, the column order needs to be sequential. There is already a column id that is auto-incremented. The first row's order should start at 1 and each additional row's order should be the next number (1,2,3, etc.)
$factory->define(App\AliasCommand::class, function (Faker\Generator $faker) {
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => (App\AliasCommand::count()) ?
App\AliasCommand::orderBy('order', 'desc')->first()->order + 1 : 1
];
});
It should be setting the order column to be 1 more than the previous row, however, it results in all rows being assigned 1.
Here's something that might work.
$factory->define(App\AliasCommand::class, function (Faker\Generator $faker) {
static $order = 1;
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => $order++
];
});
It just keeps a counter internal to that function.
Update:
Laravel 8 introduced new factory classes so this request becomes:
class AliasCommandFactory extends Factory {
private static $order = 1;
protected $model = AliasCommand::class;
public function definition() {
$faker = $this->faker;
return [
'user_id' => User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => self::$order++
];
}
}
The answer by #apokryfos is a good solution if you're sure the factory model generations will only be run in sequential order and you're not concerned with pre-existing data.
However, this can result in incorrect order values if, for example, you want to generate models to be inserted into your test database, where some records already exist.
Using a closure for the column value, we can better automate the sequential order.
$factory->define(App\AliasCommand::class, function (Faker\Generator $faker) {
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => function() {
$max = App\AliasCommand::max('order'); // returns 0 if no records exist.
return $max+1;
}
];
});
You almost had it right in your example, the problem is that you were running the order value execution at the time of defining the factory rather than the above code, which executes at the time the individual model is generated.
By the same principle, you should also enclose the user_id code in a closure, otherwise all of your factory generated models will have the same user ID.
To achieve true autoIncrement rather use this approach:
$__count = App\AliasCommand::count();
$__lastid = $__count ? App\AliasCommand::orderBy('order', 'desc')->first()->id : 0 ;
$factory->define(App\AliasCommand::class,
function(Faker\Generator $faker) use($__lastid){
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => $faker->unique()->numberBetween($min=$__lastid+1, $max=$__lastid+25),
/* +25 (for example here) is the number of records you want to insert
per run.
You can set this value in a config file and get it from there
for both Seeder and Factory ( i.e here ).
*/
];
});
In Laravel 9 (and possibly some earlier versions?), there's a pretty clean way to make this happen when you're creating models (from the docs):
$users = User::factory()
->count(10)
->sequence(fn ($sequence) => ['order' => $sequence->index])
->create();
If you'd like to start with 1 instead of 0:
$users = User::factory()
->count(10)
->sequence(fn ($sequence) => ['order' => $sequence->index + 1])
->create();
The solution also solves already data on table conditions:
class UserFactory extends Factory
{
/**
* #var string
*/
protected $model = User::class;
/**
* #var int
*/
protected static int $id = 0;
/**
* #return array
*/
public function definition()
{
if ( self::$id == 0 ) {
self::$id = User::query()->max("id") ?? 0;
// Initialize the id from database if exists.
// If conditions is necessary otherwise it would return same max id.
}
self::$id++;
return [
"id" => self::$id,
"email" => $this->faker->email,
];
}
}

Laravel preparing the database before a test for a model with many dependencies, is there a better way?

I am using Laravel 5.2 for my web app and the bundled testing tools which extend PHPUnit.
I am writing a test for a page that performs an update on a model. The test checks that one of the model's attributes hasn't changed prior to submitting the form in order to prevent two users from updating the model if they had it both open in their browser at the same time.
The test requires the use of a model in my Laravel web app, namely App\Models\Application, and I am using factories to create some fake data for this model. The Application model has many belongsTo relationships, which in turn requires that the related model is created within the test using factories prior to the creation of the Application model.
For example, the Application model has a belongsTo relationship with Applicant, so I need to create an Applicant model with a factory prior to creating an Application and an Applicant also has belongsTo relationships with User, which I also need to create before ... you get the idea.
So, in short, I am creating several model instances due to the relationship dependencies of one model which I want to test.
So this has left me wondering if I am taking the wrong approach? Is there a more simple approach where I only create the model that is under test?
Here's the code from my test:
/**
* #test
*/
public function throw_exception_when_application_status_is_modified_after_submission()
{
/**
* Arrange
*/
// create roles
factory(Role::class, 'admin')->create();
factory(Role::class, 'applicant')->create();
// create admin user
$adminUser = factory(User::class)->create();
$adminUser->attachRole(Role::whereName('admin')->first());
// create applicant user
$applicantUser = factory(User::class)->create();
$applicantUser->attachRole(Role::whereName('applicant')->first());
// create organisation with type
$organisationType = factory(OrganisationType::class)->create();
$organisation = factory(Organisation::class)->create();
$organisation->organisationTypes()->attach($organisation->id);
// create sub application
$subApplication = factory(ExperienceLetter::class)->create();
// create applicant
$applicant = factory(Applicant::class)->create([
'user_id' => $applicantUser->id
]);
// create application
$application = factory(Application::class)->make([
'status' => 'pending',
'application_id' => $subApplication->id,
'application_type' => 'App\Models\ExperienceLetter',
'organisation_id' => $organisation->id,
'applicant_id' => $applicant->id,
]);
// prep data
$data = [
'action' => 'accept',
'details' => 'email sent',
'application_id' => $application->id,
'application_status' => 'pending',
'verification' => 'email',
];
/**
* Act
*/
// log in
$this->actingAs($adminUser);
// alter status before call()
$application->status = 'verification';
$application->save();
$response = $this->call('POST', route('admin.application.action.store'), $data);
/**
* Assert
*/
$this->assertEquals(302, $response->status());
// assertSessionHasErrors
}
Thanks for any advice.
Are you creating all of those because of foreign key constraints? If you just want to check that one model without any relations then Schema::disableForeignKeyConstraints(); is your friend:
/**
* #test
*/
public function throw_exception_when_application_status_is_modified_after_submission()
{
\Schema::disableForeignKeyConstraints();
/**
* Arrange
*/
// create admin user
$adminUser = factory(User::class)->create();
$adminUser->attachRole(factory(Role::class, 'admin')->create());
// create application
$application = factory(Application::class)->make([
'status' => 'pending',
'application_id' => 0,
'application_type' => 'App\Models\ExperienceLetter',
'organisation_id' => 0,
'applicant_id' => 0,
]);
// prep data
$data = [
'action' => 'accept',
'details' => 'email sent',
'application_id' => $application->id,
'application_status' => 'pending',
'verification' => 'email',
];
/**
* Act
*/
// log in
$this->actingAs($adminUser);
// alter status before call()
$application->status = 'verification';
$application->save();
$response = $this->call('POST', route('admin.application.action.store'), $data);
\Schema::enableForeignKeyConstraints();
/**
* Assert
*/
$this->assertEquals(302, $response->status());
// assertSessionHasErrors
}

Laravel: Get the ID of User::create and insert new row using that ID

I have AuthController in Laravel and I have 2 tables, one is Users and one is Users_Information and I want to insert into Users_Information upon registration.
So I want to get the id from the following method and insert a new row and set the column ID of that row to the ID of the user I have just created.
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return User
*/
protected function create(array $data)
{
return User::create([
'username' => $data['username'] . ' ' . $data['username2'],
'mail' => $data['mail'],
'password' => bcrypt($data['password']),
]);
}
I want to insert into Users_Information with a column id, current_food and current_level
I have a controller for the Users_Information called UserInformation, would I just call UserInformation::create but how would I get the id from the User::create?
Try to use ->id of returned object, something like:
$id = $this->create($data)->id;
The create() method returns the model.
$user = User::create([
'username' => $data['username'] . ' ' . $data['username2'],
'mail' => $data['mail'],
'password' => bcrypt($data['password']),
]);
$userInfo = UserInformation::create([
'user_id' => $user->id,
'current_food' => $food,
'current_level' => $level,
]);
Suppose, I have a model name Employee and I want to insert some data in this model also want to get table id. So I can achieve this easily by below code:
$employee = new Employee();
$employee->employeeName = 'Something';
$employee->save();
$employee->id;
Eloquent has a nice way to handle saving relationships, which can be used in your case. It allows you to save a related model without accessing the model directly. Of course you must make sure your relationship is defined in the appropriate models first.
Below will create the user and their information. I assumed the method of your relationship was called information but you can adjust as needed.
$user = User::create([
'username' => $data['username'] . ' ' . $data['username2'],
'mail' => $data['mail'],
'password' => bcrypt($data['password']),
])->information()->create([
'current_food' => $current_food,
'current_level' => $current_level
]);
Notice that we did not explicitly set user_id because we simply created the information by accessing the relationship you have defined; Laravel/Eloquent handles that for you!
use insertGetId(); it gives you the id of an inserted row.
$userId = User::insertGetId([
'name' => $request->input('name'),
'email' => $request->input('email'),
'password' => bcrypt($request->input('password')),
]);
https://laravel.com/docs/5.7/queries#inserts
Also, if you are not using Eloquent, you can use insertGetId:
$id = DB::table('users')->insertGetId(
[ 'name' => 'John Doe', 'email' => 'john#example.com']
);
Remember that if you set your table with an custom ID, you must indicate so in the code. In my case, my table "Antecedentes" has the custom id "ant_id" so "id" did not work and had to put "ant_id" like this:
$success = Antecedentes::create($data)->ant_id;
And i could go on.

Categories