bastinald/malzahar

一个魔法PHP框架。无需编写HTML、CSS或JavaScript即可构建响应式Web应用程序!由Tailwind、Alpine、Laravel和Livewire提供支持。

1.0.7 2021-05-31 08:01 UTC

This package is auto-updated.

Last update: 2024-09-11 09:42:47 UTC


README

一个魔法PHP框架。无需编写HTML、CSS或JavaScript即可构建响应式Web应用程序!由Tailwind、Alpine、Laravel和Livewire提供支持。

需求

  • PHP 8
  • Laravel 8
  • NPM

安装

创建一个新的Laravel应用程序

laravel new my-app

配置.env文件中的APP、DB和MAIL值

APP_*
DB_*
MAIL_*

通过composer要求Malzahar

composer require bastinald/malzahar

运行安装命令

php artisan malz:install

这将安装并配置Tailwind和Alpine,并创建一些示例组件以供您开始。

命令

自动迁移

php artisan malz:migrate {--f} {--s} {--fs}

这将通过比较您模型的migration方法与当前数据库表结构来运行自动迁移,并应用任何必要的更改。使用--f选项进行刷新,使用--s进行种子,或使用--fs进行两者。自动迁移在Malzahar中是完全可选的。

制作Blade组件

php artisan malz:blade {class}

这将在app/Components/Blade文件夹内生成Blade抽象组件。与所有生成器命令一样,您可以使用斜杠或点表示法指定子文件夹以指定class

制作Livewire组件

php artisan malz:livewire {class} {--f}

这将在app/Components/Livewire文件夹内生成一个响应式的Livewire组件。使用--f选项制作一个包含自动路由的全页组件。

制作模型

php artisan malz:model {class}

这将生成一个Eloquent模型,包括用于与自动迁移命令一起使用的migrationdefinition方法。它还创建了一个用于该模型的工厂。

组件

Blade组件

Blade组件用于将一个或多个HTML组件抽象成它们自己的PHP类,以便可以轻松地重用和维护。

例如,假设您需要为表单制作一个可重用的Button组件

namespace App\Components\Blade\Forms;

use Bastinald\Malzahar\Components\Blade;
use Bastinald\Malzahar\Components\Html;

class Button extends Blade
{
    public $color = 'blue';

    public function attributes()
    {
        return [
            'type' => 'button',
        ];
    }

    public function classes()
    {
        return [
            'text-white rounded-md shadow-sm px-4 py-2',
            'bg-' . $color . '-600 hover:bg-' . $color . '-700',
        ];
    }

    public function template()
    {
        return Html::button($this->slot)
            ->merge($this);
    }
}

这些组件的设计与标准Laravel blade组件类似,但有一些额外的功能。注意attributesclasses方法。这是您指定组件的默认属性和类的地方。这些通过在Html::button()组件上的merge($this)方法应用。您还可以看到在上面的示例中使用了自定义属性,例如$color。属性与属性不同。注意组件模板中使用的$this->slot。slot是通过make()方法参数传递的。

现在您可以在任何其他组件中使用Button组件,通过make()方法

class Login extends Livewire
{
    public $email;

    public function template()
    {
        return GuestLayout::make(
            Html::form(
                Input::make()
                    ->type('email')
                    ->placeholder(__('Email'))
                    ->error($this->error('email'))
                    ->wireModelDefer('email'),

                Button::make(__('Login'))
                    ->color('red')
                    ->type('submit')
                    ->class('w-full'),
            )->wireSubmitPrevent('login'),
        );
    }

注意我们仍然可以通过链式方法将colortypeclass传递给Button组件,这些将与组件类内部指定的默认属性、属性和类合并。

通过Tailwind设置组件的类属性、HTML属性和CSS类。只需使用属性或属性的名称作为方法名称,其参数将是值。Tailwind类可以通过class方法应用。

Livewire组件

Livewire组件是用于创建交互式部分和完整页面的响应式组件。

全页组件应使用routetitle方法,例如

class Home extends Livewire
{
    public function route()
    {
        return Route::get('/home', static::class)
            ->name('home')
            ->middleware('auth');
    }

    public function title()
    {
        return __('Home');
    }

    public function template()
    {
        return AuthLayout::make(
            Html::h1($this->title())
                ->class('text-3xl font-bold mb-4'),

            Html::p(__('You are logged in!')),
        );
    }
}

此全页组件将通过/home路由访问。

对于部分组件,您可以在其他 Livewire 组件内部创建任何想要包含的组件,然后通过 make() 方法包含它们。您使用 make() 方法来构建所有自定义的 Blade 和 Livewire 组件。

假设我们创建了这个简单的部分组件

class Alert extends Livewire
{
    public $message;

    public function mount($message)
    {
        $this->message = $message;
    }

    public function template()
    {
        return Html::div(
            Html::p(__($this->message))
                ->class('font-bold mb-4'),

            Html::button(__('Change Message'))
                ->class('bg-blue-600 text-white px-4 py-2')
                ->wireClick('changeMessage'),
        );
    }

    public function changeMessage()
    {
        $this->message = 'I am a changed message.';
    }
}

现在我们可以在其他 Livewire 组件中通过 make() 包含,甚至声明 Alert 组件的挂载属性

class Home extends Livewire
{
    public function template()
    {
        return AuthLayout::make(
            Html::h1($this->title())
                ->class('text-3xl font-bold mb-4'),

            Alert::make()->message('Hello, world!'),
        );
    }

请注意,我们如何可以通过一个动态可用的魔法 message() 方法将 message 属性传递给 mount() 方法。Malzahar 在 Blade、Livewire 和 HTML 组件中都类似工作。你是魔法师,哈利!

哦,如果你需要获取验证错误,你可以在 Livewire 组件中使用 $this->error() 方法

use App\Components\Blade\Forms\Button;
use App\Components\Blade\Forms\Input;
use App\Components\Blade\Layouts\GuestLayout;
use Bastinald\Malzahar\Components\Html;
use Bastinald\Malzahar\Components\Livewire;

class Login extends Livewire
{
    public $email, $password;

    public function template()
    {
        return GuestLayout::make(
            Html::form(
                Input::make()
                    ->type('email')
                    ->placeholder(__('Email'))
                    ->error($this->error('email'))
                    ->wireModelDefer('email'),

                Input::make()
                    ->type('password')
                    ->placeholder(__('Password'))
                    ->error($this->error('password'))
                    ->wireModelDefer('password'),

                Button::make(__('Login'))
                    ->type('submit')
                    ->class('w-full'),
            )->wireSubmitPrevent('login'),
        );
    }

    public function rules()
    {
        return [
            'email' => ['required', 'email'],
        ];
    }

    public function login()
    {
        $this->validate();

        // attempt to log the user in
    }

HTML 组件

HTML 组件是构建所有其他组件的基础。语法类似于其他 Malzahar 组件,并且我们可以通过魔法链式方法添加任何我们想要的属性。

所有 HTML 组件都可以通过使用它们的标签作为构造函数方法来创建,该标签的属性随后跟上

public $email;

public function template()
{
    return Html::div(
        Html::h1(__('Hello, world')),

        Html::input()
            ->type('email')
            ->placeholder(__('Email'))
            ->wireModelDefer('email'),

        Html::p(__('Look ma, no hands!'))
            ->class('text-blue-600 font-bold'),
    );
}

如果你注意看,你会看到构造方法的参数包含了该 HTML 元素的槽(或内容)。看看上面的例子中的 div,它包含了一个 h1inputp。对于 h1 元素也是如此,它包含了一些翻译后的文本。

对于链式属性方法,只需指定一个 HTML 元素属性名,并将其值作为参数。看看上面的 Html::input() 例子,我们给它赋予了一个 typeemail 等。

HTML 组件也支持 Livewire 和 Alpine 方法。在上面的例子中,你可以看到使用 wireModelDefer('email'),这实际上在渲染的 HTML 中转换为 wire:model.defer="email"。这在执行操作时用于将输入值绑定到 $email 属性(defer)。

同样的概念也适用于 Alpine。假设我们想在下拉菜单中添加一些 Tailwind 过渡效果

Html::div(
    Html::button('Open Dropdown')
        ->xOnClick('open = true'),

    Html::div('Dropdown Body')
        ->xShow('open')
        ->xTransitionEnter('transition ease-out duration-100')
        ->xTransitionEnterStart('transform opacity-0 scale-95')
        ->xTransitionEnterEnd('transform opacity-100 scale-100')
        ->xTransitionLeave('transition ease-in duration-75')
        ->xTransitionLeaveStart('transform opacity-100 scale-100')
        ->xTransitionLeaveEnd('transform opacity-0 scale-95')
        ->xOnClickAway('open = false')
        ->class('absolute bg-white p-3')
)->xData('{ open: false }'),

请注意,Livewire 属性以 wire 开头,Alpine 属性以 x 开头。Malzahar 足够智能,可以在编译时将属性格式化为它们的正确语法,以便在渲染到实际 HTML 时使用。

动态组件

有时你需要在 Malzahar 应用中使用第三方 blade 组件。幸运的是,这个包通过 Dynamic 类使这一点变得非常简单。

例如,假设我安装了 Laravel Honey 包。通常,为了在其中一个视图中包含此组件,我会使用类似以下的方法

<x-honey recaptcha />

现在我们无法使用实际的 HTML 与 Malzahar 一起使用,所以我们怎么做?我们使用 Dynamic 类和魔法构造函数方法

Dynamic::honey(),

向动态组件传递属性就像添加一个链式方法一样简单

Dynamic::honey()->recaptcha(),

所有这些都与其他 Malzahar 组件类似工作。对于动态组件,构造函数方法是组件本身的名称,属性以与 HTML 或其他组件相同的方式传递。

检查你在安装 Malzahar 时创建的 NavLink 示例。你甚至可以看到 Dynamic 组件在自定义 Blade 组件内部使用 merge($this) 方法

class NavLink extends Blade
{
    public $icon, $route, $title;

    public function attributes()
    {
        return [
            'name' => 'heroicon-o-' . $this->icon,
        ];
    }

    public function classes()
    {
        return [
            'w-6 h-6',
            'text-gray-600 hover:text-black' => Route::currentRouteName() != $this->route,
            'text-blue-600' => Route::currentRouteName() == $this->route,
        ];
    }

    public function template()
    {
        return Html::a(
            Dynamic::icon()->merge($this),
        )->href(route($this->route))->title($this->title);
    }
}

语句

如果语句

使用 Malzahar 的条件 if 语句很容易

Statement::if($this->label,
    fn() => Html::label($this->label),
),

您也可以使用 elseifelse 作为 if 语句的链式方法

public $color = 'blue';

public function template()
{
    return Statement::if($this->color == 'red',
        fn() => Html::h1(__('The color is red!'))
            ->class('text-red-600'),
    )->elseif($this->color == 'blue',
        fn() => Html::h2(__('The color is blue!'))
            ->class('text-blue-600'),
    )->else(
        fn() => Html::h3(__('The color is something else!')),
    );
}

请注意,if 的第一个参数是条件,每个闭包之后是如果语句通过将渲染的内容。

每个语句

Each语句,或者像常说的循环,允许您遍历结果集并按迭代显示事物。

例如,假设我们想输出用户名单

Statement::each(User::all(),
    fn(User $user) => Html::div(e($user->name)),
)->empty(
    fn() => Html::p('No users found.'),
),

each语句的第一个参数是集合或数组。第二个参数是带有项目(可选带有键)的可调用函数。您可以使用empty方法在未找到结果时显示某些内容。

另外,注意这里的e函数是如何用于转义用户名的。通常,您会在视图文件中使用{{ $user->name }}语法,当编译时实际上只是调用e函数。

需要使用项的键?没问题

Statement::each(['red' => '#ff0000', 'green' => '#00ff00'],
    fn($hexCode, $colorName) => Html::dl(
        Html::dt($colorName),
        Html::dd($hexCode),
    )->class('mb-4'),
),

如果您有任何问题或需要帮助,请使用Github问题,我将尽快回复。感谢您查看这个包,祝您编码愉快!