code16/ozu-client

Ozu项目的配套包(https://ozu.code16.fr)

v0.4.2 2024-09-12 08:01 UTC

README

Ozu为开发并维护具有动态内容的静态网站提供了一个很好的解决方案,同时保持了Laravel的生产力和优秀的开发体验。

目前该项目处于私有测试阶段,但您可以通过以下链接请求访问权限: ozu.code16.fr。您还可以参考这篇博客文章了解更多关于该项目的信息。

安装

需要此包

composer require code16/ozu-client

发布配置文件

php artisan vendor:publish --tag="ozu-client-config"

使用方法

模型是Ozu集合

您想公开给Ozu的模型(意味着:您想配置内容管理工具的模型)必须遵循以下规则

首先,您的迁移必须使用Code16\OzuClient\Support\Database\MigratesOzuTable特质,并在up()方法中调用$this->createOzuTable('my-table');:这将创建Ozu所需列的表,然后您可以添加自己的列。

use Code16\OzuClient\Support\Database\MigratesOzuTable;
// ...

return new class extends Migration
{
    use MigratesOzuTable;

    public function up(): void
    {
        $this->createOzuTable('projects');

        Schema::table('projects', function (Blueprint $table) {
            $table->string('country')->nullable();
            // ...
        });
    }
};

基本的Ozu模型包含以下属性

  • 标题
  • 内容
  • slug(自动从标题中填充)
  • 顺序
  • 封面(作为Media,见下文)

其次,您的模型必须实现Code16\OzuClient\Eloquent\IsOzuModel特质,并实现3个静态方法

  • configureOzuCollection是您可以定义集合标签、图标以及一些选项的地方,如发布状态、可创建状态和可删除状态。
  • configureOzuCollectionList是您可以定义要在列表视图中显示的列的地方。
  • configureOzuCollectionForm是您可以定义要在表单视图中显示的自定义字段的地方。

以下是一个具有一个附加字段(country)和一些基本配置的Project模型示例

use Code16\OzuClient\Eloquent\IsOzuModel;
// ...

class Project extends Model
{
    use IsOzuModel;

    // ...

    public static function configureOzuCollection(OzuCollectionConfig $config): OzuCollectionConfig
    {
        return $config
            ->setLabel('Projects')
            ->setIcon('fa-ruler-combined')
            ->setHasPublicationState()
            ->setIsCreatable()
            ->setIsDeletable(false);
    }

    public static function configureOzuCollectionList(OzuCollectionListConfig $config): OzuCollectionListConfig
    {
        return $config
            ->addColumn(OzuColumn::makeImage('cover', 1))
            ->addColumn(OzuColumn::makeText('title', 5)->setLabel('Title'))
            ->addColumn(OzuColumn::makeText('country', 6)->setLabel('Country'))
            ->setIsSearchable()
            ->setIsReorderable();
    }

    public static function configureOzuCollectionForm(OzuCollectionFormConfig $config): OzuCollectionFormConfig
    {
        return $config
            ->addCustomField(
                OzuField::makeText('country')
                    ->setLabel('Country')
                    ->setValidationRules(['required'])
            );
    }
}

注意

此配置将由Ozu用于在内容管理工具中正确显示集合。它对您的本地代码库没有影响。

处理BelongsTo关系

一个常见的用例是在两个Ozu模型之间有一个BelongsTo关系。有两种可能性

  • 该关系不公开给Ozu,意味着您不想在CMS中处理它:在这种情况下,您可以在Laravel中像通常那样定义关系,并使用专门的外键列。
  • 如果您更可能需要允许内容管理员更新此关系,那么有一个主要约束:您只能在每个模型中有一个belongsTo关系,该关系名为parent_id

以下是一个具有属于Category模型的Project模型的示例。首先是迁移

return new class extends Migration
{
    use MigratesOzuTable;

    public function up(): void
    {
        $this->createOzuTable('projects');

        Schema::table('projects', function (Blueprint $table) {
            $table->foreignId('parent_id')->constrained('categories')->cascadeOnDelete();
            // ...
        });
    }
};

然后是Project模型

class Project extends Model
{
    use IsOzuModel;

    public function category(): BelongsTo
    {
        return $this->belongsTo(Category::class, 'parent_id');
    }
    
    // ...
    
    public static function configureOzuCollectionList(OzuCollectionListConfig $config): OzuCollectionListConfig
    {
        return $config
            // Optionally add a filter for the Project list in the CMS
            ->declareBelongsToFilter(ozuModelClass: Category::class, label: 'Saison', required: true)
            ->addColumn(/* ... */);
            // ...
    }
    
    // ...

    public static function configureOzuCollectionForm(OzuCollectionFormConfig $config): OzuCollectionFormConfig
    {
        return $config
            // Add a BelongsTo select field in the Project form in the CMS
            ->declareBelongsToField(ozuModelClass: Category::class, label: 'Project Category')
            ->addCustomField(/* ... */);
            // ...
    }
    
    // ...
}

有了这些,您可以在代码库中使用常规的$project->category关系,而Ozu将能够在CMS中的项目表单上提供一个类别选择器。

注意

当然,如果需要,您可以在类别模型中定义此关系的相反的HasMany

附加的视觉元素是Media

如果您想将图像附加到模型上,请通过Code16\OzuClient\Eloquent\Media模型使用MorphOneMorphMany关系

use Code16\OzuClient\Eloquent\Media;
// ...

class Project extends Model
{
    use IsOzuModel;

    public function visuals(): MorphMany
    {
        return $this->morphMany(Media::class, 'model')
            ->where('model_key', 'visuals')
            ->orderBy('order');
    }

    public function ogImage(): MorphOne
    {
        return $this->morphOne(Media::class, 'model')
            ->where('model_key', 'ogImage');
    }
    
    // ...
}

您必须在关系中定义model_key以区分您可以附加到模型上的不同类型的媒体。

您可以在视图中使用此关系来显示图片,并利用 thumbnail() 方法获取所需尺寸的图片URL。

@if(count($project->visuals))
    <div class="mt-12">
        <div class="grid sm:grid-cols-3 grid-cols-2 gap-4">
            @foreach($project->visuals as $visual)
                <img class="aspect-square" src="{{ $visual->thumbnail(400, fit: true) }}" alt="">
            @endforeach
        </div>
    </div>
@endif

本地(开发)种子

为了简化项目开发,您可以使用 OzuSeeder 类来为您本地的数据库填充一些示例数据。

use Code16\OzuClient\Support\Database\OzuSeeder;
// ...

class DatabaseSeeder extends OzuSeeder
{
    public function run(): void
    {
        // this will delete any remaining seeded Media file
        $this->clearMediaDirectory();

        Project::factory()
            ->count(12)
            ->has(Media::factory()->image('cover')->withFile(), 'cover')
            ->has(Media::factory()->image('visuals')->withFile()->count(3), 'visuals')
            ->sequence(fn ($sequence) => [
                'order' => $sequence->index + 1,
                'country' => fake()->country(),
            ])
            ->create();
            
        // ...
    }
}

查看示例项目以获取示例。

您可以通过访问 Ozu 示例项目 dvlpp/ozu-demo 来了解使用 Ozu 的简单项目示例。

限制

生成静态文件意味着我们不能使用请求特定的功能,如查询参数、会话、POST 表单等。但 Ozu 提供了解决方案,以尽可能让代码接近经典的 Laravel 应用。

查询字符串

考虑这个简单的用例:我们需要显示一个可排序的项目列表。在一个经典的 Laravel 应用中,我们可能会有这样的路由

Route::get('/projects')

在控制器中,我们会检查排序项目的查询参数,例如 /projects?sort=asc

在一个 Ozu 项目中,像任何静态网站一样,我们无法在控制器中检查 sort,因为我们正在生成静态 HTML 文件;您可以

  • 将查询参数放在路径参数中(例如:/projects/list/{sort}):这将创建两个 HTML 文件 projects/list/desc.htmlprojects/list/asc.html
  • 或者在前端代码中处理查询字符串(例如使用 Alpine)。

分页

出于同样的原因,?page=1 不能与生成的静态 HTML 一起使用;相反,您需要将页面作为路径段。

Route::get('/projects/index/{page}')

您仍然可以使用 {{ $projects->links() }}route('projects.index', ['page' => 2]):Ozu 覆盖了 Laravel 默认的 Paginator 来处理页面段。

会话

根据定义,会话对于静态生成的网站是不可用的。如果您确实需要存储会话数据,您可以使用 cookie 或 JavaScript 中的 localStorage。

表单

对于表单,在当前的 Ozu 状态下,您需要外部提供商来处理提交(有很多解决方案,例如 FieldGoal)。

部署到生产环境

一旦您的项目准备就绪,您就可以将其部署到 Ozu。

在 Ozu 中配置项目

首先在 config/ozu-client 配置文件中声明您的 Ozu 集合。

php artisan vendor:publish --tag="ozu-client-config"
// config/ozu-client.php

return [
    // ...
    'collections' => [
        App\Models\Project::class,
        // ...
    ],
];

然后您需要在 .env 文件中配置您的凭据。

OZU_API_KEY=[your-api-key]
OZU_WEBSITE_KEY=[your-website-key]

然后启动 ozu:configure-cms 命令。

php artisan ozu:configure-cms

此命令将为您的每个声明的模型在 Ozu 中创建一个新的集合,并将配置列表和表单根据您在模型中定义的方法进行配置。每次您添加或更新 Ozu 模型时,都必须重复此命令。

在此阶段,您应该能够在 https://ozu.code16.fr/sharp 地址看到您定制的 CMS。在这里,您可以管理集合的内容。

注意

Sharp 是 Ozu 使用的底层内容管理系统框架:尽管您实际上不需要了解它就可以使用 Ozu,但如果您好奇,可以 查看其网站

注意

目前还没有方法来填充生产数据,但它位于路线图中。

部署您的项目

一旦您已在 CMS 中输入了内容,您就可以按照以下步骤将项目作为静态网站进行部署

  1. 安装 Ozu Github 应用 + 授予您的存储库只读访问权限
  2. 在 Ozu 仪表板中配置 github 仓库 + 分支(配置菜单)
  3. 在 Netlify (https://app.netlify.com/user/applications/personal) 中创建一个新的个人访问令牌,将过期时间设置为“无过期”,并在 Ozu 仪表板中报告
  4. 在 Ozu 仪表板中创建 Netlify 网站
  5. (如果需要,将您最新的代码推送到您的存储库,并在 Ozu CMS 中调整您的内容)
  6. 点击“部署”!