• Stars
    star
    2,570
  • Rank 17,842 (Top 0.4 %)
  • Language
    PHP
  • License
    MIT License
  • Created almost 5 years ago
  • Updated 7 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Eloquent's missing "array" driver.

Sushi 🍣

Eloquent's missing "array" driver.

Sometimes you want to use Eloquent, but without dealing with a database.

This Package Is Sponsorware 💰💰💰

Originally, this package was only available to my sponsors on GitHub Sponsors until I reached 75 sponsors.

Now that we've reached the goal, the package is fully open source.

Enjoy, and thanks for the support! ❤️

Learn more about Sponsorware at github.com/sponsorware/docs 💰.

Install

composer require calebporzio/sushi

Use

Using this package consists of two steps:

  1. Add the Sushi trait to a model.
  2. Add a $rows property to the model.

That's it.

class State extends Model
{
    use \Sushi\Sushi;

    protected $rows = [
        [
            'abbr' => 'NY',
            'name' => 'New York',
        ],
        [
            'abbr' => 'CA',
            'name' => 'California',
        ],
    ];
}

Now, you can use this model anywhere you like, and it will behave as if you created a table with the rows you provided.

$stateName = State::whereAbbr('NY')->first()->name;

This is really useful for "Fixture" data, like states, countries, zip codes, user_roles, sites_settings, etc...

Relationships

Let's say you created a Role model, based on an array using Sushi, that looked like this:

class Role extends Model
{
    use \Sushi\Sushi;

    protected $rows = [
        ['id' => 1, 'label' => 'admin'],
        ['id' => 2, 'label' => 'manager'],
        ['id' => 3, 'label' => 'user'],
    ];
}

You can add a relationship to another standard model, just like you normally would:

class User extends Model
{
    ...

    public function role()
    {
        return $this->belongsTo(Role::class);
    }
}

Assuming the users table has a role_id column, you can do things like this:

// Grab a User.
$user = User::first();
// Grab a Role.
$role = Role::whereLabel('admin')->first();

// Associate them.
$user->role()->associate($role);

// Access like normal.
$user->role;

// Eager load.
$user->load('role');
User::with('role')->first();

Note: There is one caveat when dealing with Sushi model relationships. The whereHas method will NOT work. This is because the two models are spread across two separate databases.

Using database-checking validation rules

You can even use Laravel's exists:table,column database checking request validation rule.

$data = request()->validate([
    'state' => ['required', 'exists:App\Model\State,abbr'],
]);

Note: Be aware that you must use the fully-qualified namespace of the model instead of a table name. This ensures that Laravel will correctly resolve the model's connection.

Custom Schema

If Sushi's schema auto-detection system doesn't meet your specific requirements for the supplied row data, you can customize them with the $schema property or the getSchema() method.

class Products extends Model
{
    use \Sushi\Sushi;

    protected $rows = [
        ['name' => 'Lawn Mower', 'price' => '226.99'],
        ['name' => 'Leaf Blower', 'price' => '134.99'],
        ['name' => 'Rake', 'price' => '9.99'],
    ];

    protected $schema = [
        'price' => 'float',
    ];
}

How It Works

Under the hood, this package creates and caches a SQLite database JUST for this model. It creates a table and populates the rows. If, for whatever reason, it can't cache a .sqlite file, it will default to using an in-memory sqlite database.

Using ->getRows()

You can optionally opt out of using the protected $rows property, and directly implement your own getRows() method.

This will allow you to determine the rows for the model at runtime. You can even generate the model's rows from an external source like a third-party API.

class Role extends Model
{
    use \Sushi\Sushi;

    public function getRows()
    {
        return [
            ['id' => 1, 'label' => 'admin'],
            ['id' => 2, 'label' => 'manager'],
            ['id' => 3, 'label' => 'user'],
        ];
    }
}

Caching ->getRows()

If you choose to use your own ->getRows() method, the rows will NOT be cached between requests by default.

You can force Sushi to cache your dataset with the following method: sushiShouldCache().

Let's look at a configuration where ->getRows() datasets would be cached as an example:

class Role extends Model
{
    use \Sushi\Sushi;

    public function getRows()
    {
        return [
            ['id' => 1, 'label' => 'admin'],
            ['id' => 2, 'label' => 'manager'],
            ['id' => 3, 'label' => 'user'],
        ];
    }

    protected function sushiShouldCache()
    {
        return true;
    }
}

By default, Sushi looks at the "last modified" timestamp of your model PHP file and compares it with its internal .sqlite cache file. If the model file has been changed more recently than the .sqlite cache file, then Sushi will destroy and rebuild the .sqlite cache. Additionally, you can configure an external file for Sushi to reference when determining if the cache is up to date or needs to be refreshed.

If, for example, you are using Sushi to provide an Eloquent model for an external data source file like an .csv file, you can use sushiCacheReferencePath to force Sushi to reference the .csv file when determining if the cache is stale.

For example:

class Role extends Model
{
    use \Sushi\Sushi;

    public function getRows()
    {
        return CSV::fromFile(__DIR__.'/roles.csv')->toArray();
    }

    protected function sushiShouldCache()
    {
        return true;
    }

    protected function sushiCacheReferencePath()
    {
        return __DIR__.'/roles.csv';
    }
}

Now, Sushi will only "bust" its internal cache if roles.csv changes, rather than looking at the Role.php model.

Handling Empty Datasets

Sushi reads the first row in your dataset to work out the scheme of the SQLite table. If you are using getRows() and this returns an empty array (e.g an API returns nothing back) then Sushi would throw an error.

If you would like Sushi to work even if the dataset is empty, you can define your schema in the optional protected $schema array.

Note: If you choose to use your own ->getRows() method, the rows will NOT be cached between requests.

class Currency extends Model
{
    use \Sushi\Sushi;

    protected $schema = [
        'id' => 'integer',
        'name' => 'string',
        'symbol' => 'string',
        'precision' => 'float'
    ];

    public function getRows()
    {
        return [];
    }
}

Handling String-based Primary Keys

Sushi requires you to add two properties to your model, if it uses a string-based primary key - $incrementing and $keyType:

class Role extends Model
{
    use \Sushi\Sushi;
    
    public $incrementing = false;

    protected $keyType = 'string';

    protected $rows = [
        ['id' => 'admin', 'label' => 'Admin'],
        ['id' => 'manager', 'label' => 'Manager'],
        ['id' => 'user', 'label' => 'User'],
    ];
}

Troubleshoot

ERROR: SQLSTATE[HY000]: General error: 1 too many SQL variables

By default Sushi uses chunks of 100 to insert your data in the SQLite database. In some scenarios this might hit some SQLite limits. You can configure the chunk size in the model: public $sushiInsertChunkSize = 50;

More Repositories

1

awesome-helpers

Helper functions I find super-duper handy
PHP
629
star
2

onboard

A Laravel package to help track user onboarding steps.
PHP
439
star
3

gitdown

A simple package to parse Github Flavored Markdown in PHP
PHP
219
star
4

better-phpunit

A better PHPUnit test runner for VS Code
TypeScript
211
star
5

laracasts-livewire-datatable

The Laravel project I used during the "Building DataTables with Livewire" Laracasts video
PHP
118
star
6

bear-sync

Access your Bear notes in Laravel
PHP
107
star
7

tailbuild

A simple command to build a Tailwind CSS file for your project (with JIT compiling and watching)
JavaScript
89
star
8

laravel-helpers-file

Because I can never remember exactly how to autoload my helpers.php file.
PHP
61
star
9

laravel-frontend-preset

My personal frontend preset for new laravel applications.
PHP
31
star
10

laracon-online-2020

The Laravel project used in my Laracon Online 2020 talk
PHP
28
star
11

laracasts-building-alpine

JavaScript
27
star
12

simple-php-cs-fixer

A VS Code extension for simple php-cs-fixer integration
PHP
24
star
13

sps

TypeScript
20
star
14

vue-form-state

JavaScript
19
star
15

vercel-laravel

PHP
19
star
16

click

The podcast recording and editing suite of your dreams. Featuring just the one button.
Crystal
18
star
17

livewire-uncovered

PHP
13
star
18

laracasts-turbolinks

The source app from my Laracasts turbolinks video.
PHP
11
star
19

usesushi.dev

The website for sushi
PHP
10
star
20

presettings

A VS Code extension for storing and activating settings presets
TypeScript
10
star
21

laracasts-server-fetched-partials

The Laravel app used in my Laracasts episode on "Server-Fetched Partials"
PHP
10
star
22

laracasts-cached-server-fetched-partials

The finished Laravel app from my "Caching Server Fetched Partials" video.
PHP
10
star
23

vue-fetch-html

A little component for fetching html/vue/javascript from the server.
Vue
9
star
24

livewire-bot

Livewire's GitHub bot
PHP
7
star
25

laravel-acceptance-example

An example laravel project with acceptance tests that render javascript and work well with laravel's helpers.
PHP
7
star
26

model-inheritance

PHP
6
star
27

99bottles-php

PHP
4
star
28

edoc-2019

PHP
4
star
29

vue-example-component

For when you regret getting rid of the default Laravel ExampleComponent.vue
Vue
3
star
30

screendit

PHP
3
star
31

calebporzio

3
star
32

johnny

HTML
2
star
33

dotfiles

Shell
2
star
34

write-less-js

PHP
1
star
35

advent-of-code-2018

My Advent Of Code solutions for 2018
PHP
1
star
36

rm-me

1
star
37

spend-mo

JavaScript
1
star
38

psrpoetry

HTML
1
star
39

ingenuity

CSS
1
star
40

ingenuity-teaser

HTML
1
star
41

theretireddevtheme

A modified ghost (blog) theme for theretireddev.com
CSS
1
star