Skip to content

BrandonKerr/eloquent-from-settings

Repository files navigation

Eloquent From Settings

Packagist Version Tests Static Analysis codecov

This package allows you to easily build Eloquent models and their relationships, through data settings via array or JSON passed to the model's factory.

Installation

Install the package via composer:

composer require brandonkerr/eloquent-from-settings

First, ensure that your model has a factory:

namespace App\Models;

// ...
use Illuminate\Database\Eloquent\Factories\HasFactory;

class User extends Authenticatable 
{
    use HasFactory;
    // ...
}

Then simply add the FromSettings trait to your factory:

namespace Database\Factories;

// ...
use Brandonkerr\EloquentFromSettings\Traits\FromSettings;

class UserFactory extends Factory
{
    use FromSettings;
    // ...
}

Configuration

By default, the FromSettings trait behaves strictly and will throw:

  • a MissingTraitException if a keyed relationship does not use the FromSettings trait
  • an UnknownKeyException if a key cannot be matched to an attribute, relationship, or function

While this is the recommended behaviour to ensure data integrity, you have the freedom to change these settings.

If you would like to allow for a keyed relationship that does not use the FromSettings trait, you can either set the $throwsMissingTraitException property to false:

class FooFactory extends Factory
{
    use FromSettings;
    
    public bool $throwsMissingTraitException = false;
    
    // ...
}

or for a customized logic, implement FromSettingsInterface and complete the getThrowsMissingTraitException() function:

class BarFactory extends Factory implements FromSettingsInterface
{
    use FromSettings;
    
    public function getThrowsMissingTraitException(): bool
    {
        return $this->someCustomSettingOrLogic;
    }
    
    // ...
}

If you would like to allow for unknown keys to be silently dropped, you can either set the $throwsUnknownKeyException property to false:

class FooFactory extends Factory
{
    use FromSettings;
    
    public bool $throwsUnknownKeyException = false;
    
    // ...
}

or for a customized logic, implement FromSettingsInterface and complete the getThrowsUnknownKeyException() function:

class BarFactory extends Factory implements FromSettingsInterface
{
    use FromSettings;
    
    public function getThrowsUnknownKeyException(): bool
    {
        return $this->someCustomSettingOrLogic;
    }
    
    // ...
}

Usage

Simply pass the desired settings via array to the factor's fromSettingsArray function:

$data = [
        "name" => "Brandon Kerr",
        "books" => [
            [
                "title" => "How to Use Eloquent From Settings",
                "reviews" => [
                    [
                        "reviewer" => "Jane Doe",
                        "score" => 80,
                    ],
                    [
                        "reviewer" => "John Smith",
                        "score" => 45,
                    ],
                ],
            ],
        ],
    ];
Author::factory()->fromSettingsArray(...$data)->create();

or via JSON to the factor's fromSettingsJson function:

$json = '{
   "name":"Brandon Kerr",
   "books":[
      {
         "title":"How to Use Eloquent From Settings",
         "reviews":[
            {
               "reviewer":"Jane Doe",
               "score":80
            },
            {
               "reviewer":"John Smith",
               "score":45
            }
         ]
      }
   ]
}';
Author::factory()->fromSettingsJson($json)->create();

The end result will be

  • an Author model with name Brandon Kerr
  • a Book model whose Author is created above and title is How to Use Eloquent From Settings
  • a Review for the Book created above with reviewer Jane Doe and a score of 80
  • a Review for the Book created above with reviewer John Smith and a score of 45

Full Example

This example covers Authors writing Books, which have Reviews. Full details can be found in the tests directory of this package.

Models and Schemas

Refer to the Models and Migrations directories under tests/Stubs for full details.

Author

Column/Attribute Type Notes
id unsigned bigint Primary Key
name string unique constraint added for use in a custom function test
  • an Author HasMany Books
  • an Author HasManyThrough Reviews (through Books)

Book

Column/Attribute Type Notes
id unsigned bigint Primary Key
title string title of the book
author_id unsigned bigint Foreign Key to Author
  • a Book BelongsTo an Author
  • a Book HasMany Reviews

Review

Column/Attribute Type Notes
id unsigned bigint Primary Key
reviewer string name of the reviewer
score integer the score (out of 100)
book_id unsigned bigint Foreign Key to Book
  • a Review BelongsTo a Book

Factories

The AuthorFactory and ReviewFactory classes are completely standard (aside from using the FromSettings trait), but the BookFactory has two custom functions to showcase additional functionality of this package:

 /**
 * Custom function to find or create the author, based on the given name
 *
 * @param string $name
 * @return $this
 */
public function forAuthor(string $name): self
{
    $author = Author::firstOrCreate([
        "name" => $name
    ]);

    return $this->state(["author_id" => $author->id]);
}

/**
 * Custom function to add reviews with a perfect score
 *
 * @param string ...$names
 * @return $this
 */
public function perfectReviews(string ...$names): self
{
    return $this->has(
        Review::factory()
            ->count(count($names))
            ->sequence(fn (Sequence $sequence) => [
                "reviewer" => $names[$sequence->index],
                "score" => 100,
            ])
    );
}

Settings Data

Below is a commented example of an array of settings, where the author is the root of the data:

$data = [
        // the author's name attribute
        "name" => "Bob",
        // the author's HasMany relationship with Book
        "books" => [
            // first book
            [  
                // the book's title attribute
                "title" => "Bob's First Book",
                // the book's HasMany relationship with Review
                "reviews" => [
                    // first review
                    [
                        // the review's reviewer attribute
                        "reviewer" => "Jane Doe",
                        // the review's score attribute
                        "score" => 80,
                    ],
                    // second review
                    [
                        // the review's reviewer attribute
                        "reviewer" => "John Smith",
                        // the review's score attribute
                        "score" => 45,
                    ],
                ],
            ],
            // second book
            [
                // the book's title attribute
                "title" => "Please Don't Review Me",
                // NOTE no reviews 
            ],
        ],
    ];

Then we simply call the Author's factory:

$author = Author::factory()->fromSettingsArray(...$data)->create();

The end result is

  • One Author with name Bob, which has two books:
    • The first book is titled Bob's First Book and has two reviews:
      • one with reviewer Jane Doe and a score of 80, and
      • one with reviewer John Smith and a score of 45.
    • The second book is titled Please Don't Review Me and has zero reviews.

BelongsTo

We aren't limited to Has__ relationships, like in the example above. We can also use a Book as the data root, and create an author for it:

$data = [
    "title" => "My Book",
    "author" => [
        "name" => "Bob Jones",
    ],
];
Book::factory()->fromSettingsArray(...$data)->create();

The end result is one Author named Bob Jones, which has one Book titled My Book.

Custom Functions

But what if we don't want to create a new author, and don't know the author's ID that we could use to set the book's author_id value? You could create a custom function on the Book's factory that can find the author by their name, and assign the author to the book. See the forAuthor() function above for details.

Author::create([
    "name" => "Bob Jones"
]);
// ...
$data = [
    "title" => "My Book",
    "forAuthor" => [
        "name" => "Bob Jones",
    ],
];

$book = Book::factory()->fromSettingsArray(...$data)->create();

The end result is again one Author named Bob Jones, which has one Book titled My Book, but since the Author named Bob Jones already existed, it would not be created again, and it would not violate our unique name constraint on the Authors table.

We can also use a custom function to accept non key-value pair settings. See the perfectReviews() function above for details.

$data = [
    "title" => "My Book",
    "author" => [
        "name" => "Bob Jones",
    ],
    "perfectReviews" => [
        [
            "Jane Doe",
        ],
        [
            "John Smith",
        ],
    ],
];

The end result is once again one Author named Bob Jones, which has one Book titled My Book. However, this time the Book has two Reviews: one with reviewer Jane Doe, and the other with reviewer John Smith, and both with a score of 100 (as set by the perfectReviews function).

License

This package is open-sourced software licensed under the MIT license.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages