macropage/sushi

Eloquent 缺失的 "array" 驱动器。

资助包维护!
calebporzio

dev-master 2023-02-06 02:48 UTC

This package is auto-updated.

Last update: 2024-09-06 06:40:32 UTC


README

我不明白这对有原始代码的人来说是如何工作的。
有了这个更改,我可以从任何 mysql-query 中创建模型。

$QueryResult = new QueryResultModel();
// $connection_name from config/database.php
$QueryResult->initbootSushi($connection_name, $sql_query_string);
<?php

namespace App\Models;

use DB;
use Illuminate\Database\Eloquent\Model;
use Sushi\Sushi;

class QueryResult extends Model
{
    use Sushi;

    private ?string $use_connection = NULL;
    private ?string $use_query = NULL;

    public function getRows(): array
    {
        $result = DB::connection($this->use_connection)->select($this->use_query);
        return array_map(fn($x) => (array)$x, $result);
    }

    /**
     * @param mixed $use_query
     */
    public function setUseQuery(string $use_query): void
    {
        $this->use_query = $use_query;
    }

    /**
     * @param mixed $use_connection
     */
    public function setUseConnection(string $use_connection): void
    {
        $this->use_connection = $use_connection;
    }

}

Sushi 🍣

Eloquent 缺失的 "array" 驱动器。

有时你想使用 Eloquent,但又不想处理数据库。

此包是赞助软件 💰💰💰

最初,此包仅在 GitHub Sponsors 的赞助者中可用,直到我达到 75 位赞助者。

现在我们已经达到目标,该包现在是完全开源的。

享受,并感谢支持! ❤️

github.com/sponsorware/docs 上了解更多关于 赞助软件 的信息 💰。

安装

composer require calebporzio/sushi

使用

使用此包包括两个步骤

  1. Sushi 特性添加到模型中。
  2. $rows 属性添加到模型中。

就这样。

class State extends Model
{
    use \Sushi\Sushi;

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

现在,您可以在任何地方使用此模型,它将表现得像您创建了包含您提供的行的表一样。

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

这对于 "Fixture" 数据(如国家、地区、邮编、用户角色、站点设置等)非常有用。

关系

假设您创建了一个基于数组使用 Sushi 的 Role 模型,如下所示

class Role extends Model
{
    use \Sushi\Sushi;

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

您可以将关系添加到另一个标准模型,就像您通常做的那样

class User extends Model
{
    ...

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

假设 users 表有一个 role_id 列,您可以这样做

// 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();

注意:处理 Sushi 模型关系时有一个注意事项。 whereHas 方法将不会工作。这是因为两个模型分布在两个不同的数据库中。

使用数据库检查验证规则

您甚至可以使用 Laravel 的 exists:table,column 数据库检查请求验证规则。

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

注意:请注意,您必须使用模型的完全限定命名空间而不是表名。这确保 Laravel 正确解析模型的连接。

自定义模式

如果 Sushi 的模式自动检测系统不能满足您对提供的行数据的特定要求,您可以使用 $schema 属性或 getSchema() 方法来自定义它们。

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',
    ];
}

工作原理

在底层,此包为该模型创建并缓存了一个 SQLite 数据库。它创建一个表并填充行。如果由于任何原因无法缓存 .sqlite 文件,它将默认使用内存中的 sqlite 数据库。

使用 ->getRows()

您可以选择不使用 protected $rows 属性,并直接实现自己的 getRows() 方法。

这将允许您在运行时确定模型的行。您甚至可以从第三方 API 等外部源生成模型的行。

class Role extends Model
{
    use \Sushi\Sushi;

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

缓存 ->getRows()

如果您选择使用自己的 ->getRows() 方法,则默认情况下不会在请求之间缓存行。

您可以使用以下方法强制 Sushi 缓存您的数据集:sushiShouldCache()

以下是一个示例配置,其中 ->getRows() 数据集将被缓存

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;
    }
}

默认情况下,Sushi会查看您的模型PHP文件的“最后修改”时间戳,并将其与内部的.sqlite缓存文件进行比较。如果模型文件比.sqlite缓存文件更早被修改,那么Sushi将销毁并重建.sqlite缓存。此外,您还可以配置一个外部文件,让Sushi在确定缓存是否是最新的或需要刷新时参考。

例如,如果您使用Sushi为外部数据源文件(如.csv文件)提供一个Eloquent模型,您可以使用sushiCacheReferencePath强制Sushi在确定缓存是否过时时参考.csv文件。

例如

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';
    }
}

现在,只有当roles.csv发生变化时,Sushi才会“破坏”其内部缓存,而不是查看Role.php模型。

处理空数据集

Sushi会读取数据集的第一行以确定SQLite表的方案。如果您使用getRows()并且这返回一个空数组(例如API没有返回任何内容),那么Sushi会抛出错误。

如果您希望即使数据集为空,Sushi也能正常工作,您可以在可选的protected $schema数组中定义您的方案。

注意:如果您选择使用自己的->getRows()方法,则请求之间不会缓存行。

class Currency extends Model
{
    use \Sushi\Sushi;

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

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

处理基于字符串的主键

如果模型使用基于字符串的主键,Sushi要求您在模型中添加两个属性:$incrementing$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'],
    ];
}

故障排除

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

默认情况下,Sushi使用100个数据块来将数据插入SQLite数据库。在某些情况下,这可能会触及SQLite的限制。您可以在模型中配置数据块大小:public $sushiInsertChunkSize = 50;