1

I am working on application that is made up of Leads, each Lead -> hasMany -> Fields. My application will accept an infinite amount of whitelisted fields. Some Leads will have a lot of Fields, others will have maybe around 5. Due to this, I've opted for the Field table to be vertical, rather than fit in all the fields I accept horizontally, and then run into MySQL errors down the line (table too wide, etc.)

Here's my structure:

Lead Table

id
...

Field Table:

id
lead_id
field_name
field_value

I have created a model factory for my Lead model, that automatically creates 5 random fields using Faker.

I have an array of texts, numbers, dates (etc) fields which my application accepts.

Field Factory:

...

$texts = config('fields.whitelist.text');
foreach ($texts as $text) {
    $fields[$text] = $faker->sentence();
}

...

$randomField = array_random(array_keys($fields));    

return [
    'field_name' => $randomField,
    'field_value' => $fields[$randomField],
];

I've been doing this:

$lead = factory(Lead::class)->create()
         ->each(function ($l) {
             $l->fields()->save(factory(Field::class, 5)->make());
         });

However, I now have a minimum array of Fields which each Lead must have. I have these minimum fields in another config.

Is it possible to automatically create the x minimum Fields on the vertical table, using a factory?

E.g.

Minimum Fields

first_name
date_of_birth

How can I write a factory to automatically create the following structure:

[
    'field_name' => 'first_name',
    'field_value' => '<random name>',
],
[
    'field_name' => 'date_of_birth',
    'field_value' => '<random date>',
],

Edit: and if possible, not insert duplicate field_name values. Not like it's 100% deal breaker, but I want to make sure I 100% know what data I am working with, so checking x number of duplicates I imagine would be a nightmare

Kingsley
  • 977
  • 2
  • 11
  • 27

1 Answers1

2

If you want each Lead to have those minimum fields, then add those fields to your each() closure. Like this:

$lead = factory(Lead::class)->create()->each(function ($lead) {
    $lead->fields()->createMany([
        'field_name' => 'first_name',
        'field_value' => $faker->firstName,
    ],
    [
        'field_name' => 'date_of_birth',
        'field_value' => $faker->dateTime(),
    ]);
    $lead->fields()->save(factory(Field::class, 3)->make());
});

I changed the Field factory to 3 because there are 2 fields from the "minimum fields" that are inserted for every Lead.

Erik Berkun-Drevnig
  • 2,306
  • 23
  • 38
  • Ah - fantastic, I didn't think about that! However, a new issue that I've realised, would there be a way to avoid creating duplicates of the same field? – Kingsley Mar 12 '18 at 19:00
  • What do you mean by duplicates? Faker should prevent that the field_values are not duplicated but I thought the intention was for the field_name should be duplicated for each Lead. – Erik Berkun-Drevnig Mar 12 '18 at 19:11
  • I don't want the same `field_name` to be entered more than once for the same Lead. I have validation to prevent it in my controller, but I'm trying to unit test the same method – Kingsley Mar 12 '18 at 19:36
  • You can add a unique constraint in your database for that – Erik Berkun-Drevnig Mar 12 '18 at 19:43
  • Yeah I had originally thought about that, but then we have some other business requirements where a `Lead` may contain the same `Field` (same `field_name`), so that wouldn't work for us – Kingsley Mar 12 '18 at 19:46
  • It would work, you just need the unique constraint to be on multiple columns, most likely a combination of lead_id and field_name – Erik Berkun-Drevnig Mar 12 '18 at 19:47
  • Wouldn't it run into MySQL constraint issues (occasionally or constantly, not 100% sure) as Laravel wouldn't be aware of the unique mysql constraint, would it? – Kingsley Mar 12 '18 at 20:44
  • 1
    Faker has the `->unique()` method just for this https://github.com/fzaninotto/Faker – jeremykenedy Mar 12 '18 at 21:01
  • Sorry, I think there's a bit of confusion. I am not concerned about duplicate random `values`, I am concerned about duplicate `keys`. I do not want to insert the same `field_name` more than once – Kingsley Mar 13 '18 at 19:44
  • You can enforce that at the database level with `$table->unique('field_name')` if you want it to be unique per `Lead` then do `$table->unique(['field_name', 'lead_id'])`. As far as Laravel being aware of this uniqueness, the `unique()` method in faker takes care of that. Checking constraints elsewhere in your application is outside the scope of this question but Laravel does have facilities for that. – Erik Berkun-Drevnig Mar 13 '18 at 20:34