Seeding a test database in Laravel using factories and a deterministic seed - laravel

I have a number of factories generating models using faker, as recommended in the documentation:
$factory->define(App\Member::class, function (Faker $faker) {
return [
'name' => $faker->name()
];
});
I would like to use these factories in tests in order to seed the database into a known state. Faker (by its nature) is creating random data when I call factory(\App\Member::class, 10) which makes an assertion like $this->assertEquals('Eve', Member::find(5)->name) fail on future runs.
I note that Faker has a seed method to allow deterministic data generation:
$faker = Faker\Factory::create();
$faker->seed(1234);
However, with the factory(\App\Member::class, 10) interface there appears to be no way to set the seed of the Faker instance used in the factory.
Is there a way to set the Faker seed from the test case?
Or failing this what at the best practices for setting the database state in a test (I would like to use factories but maybe this is not the best approach)?

Faker is used with your factories in order to allow you to quickly create model instances without having to provide all the data yourself within your tests. It's much easier to simply do:
factory(User::class)->create();
...than it is to manually specify all of the fields that the User model requires. Faker provides random, sample data for all of the fields as specified in your factory definition.
When asserting in your tests, you shouldn't rely on knowing what that random data will be ahead of time. You can provide attributes yourself, which are merged into the random data defined in your factory and you can use this data to make your assertions.
A simple, trivial example:
$user = factory(User::class)->create(['name' => 'Joe Bloggs']);
$this->assertEquals('Joe Bloggs', $user->name);
The benefit of this is that you only need to provide the attributes you're interested in for your test and you can let your factory take care of providing the rest of the data.
You could of course allow your factory to provide the attributes and then use that information in the generated model(s) to check the data in your database. Something like this:
$user = factory(User::class)->create(['enabled' => false]);
$user->enableUser();
$this->seeInDatabase((new User)->getTable(), [
'id' => $user->id,
'name' => $user->name,
'enabled' => true
]);
In this example, the factory is used to create a user, providing the enabled attribute to be false. The method you're testing, in this case enableUser is run. Then you can use the ID and the name of the user generated by the factory in your where part of seeInDatabase, along with 'enabled' => true to ensure that the database was updated for the generated user, by setting the enabled field to true but leaving the generated name unchanged.

Related

Create new user in database but keep password if it exists in Laravel

I am wondering if the updateOrCreate method in Laravel has a way to skip some information in case the record exists in the database. For example, I wanna be able to search for users, and assign a password if the user is to be created, but not if the user already exists.
Say, I have 3 data for a user, $email, $info and $password. The search criteria would of course be $email, then maybe $info needs to be updated for whatever reason, while $password should only be given if the record is new.
Is there a 'do not update' parameter for updateOrCreate where I can put the password?
User::updateOrCreate(
[
'email' => $email // These parameters are the search criteria, but also used on create
],
[
'data' => $info, // These parameters are the values that will be updated if exist
],
[
'password' => $password, // These parameters are used only in create but are not search criteria
]
);
There are so many questions I would have love to ask to fully understand your organization's process.
1.How did the information get to the db at the first place if the user has not be created?
2. Why can't the password be generated when creating the info into the db at the first place
But I guess all these might bore down to design issue so, I think would say you can create a regular expression that would match your organization document number pattern and use it to check for new user through a loop and the assign password to such ones.

Has laravel 7 model a way to get list of all columns?

Has laravel 7 model a way to get list of all columns ?
I found and tried method
with(new ModelName)->columns
but it returns empty string.
I do not mean $fillable var of a model.
Thanks!
If you just want a reliable way to pull the list of attributes from any given instance no matter the state, and assuming the table structure isn't changing often, the path of least resistance might be to set a defaults attributes array to ensure the attributes are always present.
e.g.
class Fish extends Model
{
protected $attributes = [
'uuid' => null,
'fin_count' => null,
'first_name' => null,
];
}
$fishie = app(\Fish::class);
will then result in an instance of Fish with uuid, fin_count, and first_name set. You can then use $fishie->attributes or $fishie->getAttributes() to load the full set.
Assuming the structure doesn't change a lot, setting the attributes on the model like this will save you a database query every time you want to reference the list. The flip side is that instances change from not having the attributes unless explicitly defined to always being present, which may have implications in the project.
Here's the documentation for default attributes:
https://laravel.com/docs/master/eloquent#default-attribute-values

Hide API Resource Field / Belongs To Many Through Deep Relationship Problem

It seems as if the tutorial right here doesn't work as intended https://hackernoon.com/hiding-api-fields-dynamically-laravel-5-5-82744f1dd15a
I am aware of how to load relationships only if necessary by making use of "whenLoaded", but for simple field parameters that do not involve a relationship, I am unable to dynamically load these fields.
Below is a snippet of Agency.php
'expected_num_of_b_positive_donors' => $this->getExpectedNumOfDonors("B+"),
'elligible_to_schedule_mbd' => $this->
'donors' => $this->getDonors(),
'contact_persons' => $this->getContactPersons(),
'last_mbd' => $this->getLastMBD(),
'average_number_of_donors_per_day' => $this->getAverageNumberofDonorsPerDay()
This would all have been extremely easy, if Laravel came out of the box with a Belongs To Many Through Deep Relationship, as I could have easily used:
'donors' => $this->whenLoaded("donors")
instead of:
'donors' => $this->getDonors(),
For context, here is a snippet of my getDonors()
$donors = [];
// MBD -> Schedule -> Donation List -> Donor
foreach($this->mbds as $mbd){
foreach($mbd->mbd_schedules as $schedule){
foreach($schedule->donation_list as $donation_list){
if(!in_array($donation_list->donation->donor, $donors)){
array_push($donors, new UserResource($donation_list->donation->donor));
}
}
}
}
return $donors;
Since Belongs To Many Through does not exist, I had to create a custom laravel relationship where I can get All Donors of an Agency. Now, I have 2 Questions.
Is there a way for me to dynamically load the getDonors()?
// I am aware that this is wrong, I'd just like to give context if it's possible to do something like this
'donors' => $this->whenLoaded("donors", $this->getDonors()),
Is there an elegant way for me to make a custom Belongs To Many Relationship? So that I could just simply do
'donors' => $this->whenLoaded("donors")
I am aware that third party packages exist for Belongs To Many Relationships, but I'd like to know which one of these are best in your opinions as I am afraid of using a potentially wrong package and end up screwing my system much more.
Thanks Laracasts!

Symfony2: How do i validate an object/array with its constraints in another object/array?

It's built dynamically, annotations are not an option. I have the constraints in an array.
It seems you need to manually construct the validator but I get stuck fast on the point where you need to construct a MetaDataFactory that needs an loader, but all the loaders have the job of loading meta data from a file.. I dont think I'm doing it right.
Simply said:
I have an array/object with keys/values. I want them validated by another array that contains the constraints.
It's not possible to iterate over the keys and call validate on each and every one of them, since some rely on the context values from other keys.
$array = [
'key1' => 'abc',
'key2' => 'def',
];
$constraints = [
'key1' => new All([
new Constraint..,
new CallbackConstraint.., // <- this one checks key2 for its value
]),
'key2' => new NotBlank
];
I can also build one array containing both.
Againt, the object/array is built dynamically, i cant validate the container itself. It's about the keys inside it that are definitions on its own.
So what i basically would want is:
$validator->validate($array, $constraints);
I can imagine you need a custom MetaDataFactory / Loader / Context class or something, the problem is simply that the callback validator needs to access $this->getRoot() to get to the other values
Nevermind that, you would need something like a CallbackLoader in which you create your own properties => constraints mapping. But the LoaderInterface requires a concrete implementation of ClassMetaData, which on his own has no way of dealing with arrays or ArrayObjects, unless you have methods/properties on that ArrayObject. But--since my container is built dynamically, i can't have that. Method methods are not an option because of the property/method_exists calls.

Yii2, few tables with uncomfortable generic field names for 1 Model

I'm building REST++ service with Yii2, which should be able to deal with 2 table schemes, (databases storing ~the~same~ data):
a_new_one, which is perfect to be handled with ActiveRecord;
the_old_one, which has 1 generic table 'Object' with columns like 'field1', 'field2', etc, and few additional tables.
I want my request methods and controllers to be all the same for both schemes (would be very great, really). That means, that I need to have THE SAME MODELS for both cases.
The troubles all relate to the_old_one database:
Let's say data of model User is stored in tables Object and UserProfile. If I make 2 ActiveRecord classes for both tables, will it be possible to make hasOne() relations to them in 3d class User (not sure about inheritance here), so it can be manipulated like this:
$user = User::find()->someFilters()->someOrderingAndPagination()->one();
$user->Name = $your_new_name;
$user->Age = $why_not_90;
$user->save();
// and so on.
Because I really don't want to be forced to write it like this:
$user = User::find()->notSureAboutRelations()
->filters()->ordering()->pagination()->one();
$user->object->field7 = $your_new_name;
$user->userProfile->Age = $why_not_90;
$user->save();
I learned, Doctrine has easy model mapping ($user->Name can be mapped to db's Object.field7), but it's still impossible to map to 2 different tables within 1 Entity. Or am I incorrect ? If so, would it be a good idea to use Doctrine within Yii2 ?
So finished with idea to have the same controllers and models. Only URLs remain the same.
The old db Model data could be gained this way:
$user = User::find()
->select([
'Object.field7 as Name',
'UserProfile.Age as Age',
// ...
])->from('Object')
->join('UserProfile', ['UserProfile.ObjectId = Object.Id'])
->andWhere('User.Id' => $id);
And it's an ugly mess up when saving this models. Cos you need to assign a lot of fields in BEFORE_INSERT, BEFORE_UPDATE events.

Resources