bastinald / malzahar
一个魔法PHP框架。无需编写HTML、CSS或JavaScript即可构建响应式Web应用程序!由Tailwind、Alpine、Laravel和Livewire提供支持。
Requires
- php: ^8.0
- blade-ui-kit/blade-heroicons: ^1.2
- doctrine/dbal: ^3.0
- laravel/framework: ^8.0
- livewire/livewire: ^2.0
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模型,包括用于与自动迁移命令一起使用的migration
和definition
方法。它还创建了一个用于该模型的工厂。
组件
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组件类似,但有一些额外的功能。注意attributes
和classes
方法。这是您指定组件的默认属性和类的地方。这些通过在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'), ); }
注意我们仍然可以通过链式方法将color
、type
和class
传递给Button
组件,这些将与组件类内部指定的默认属性、属性和类合并。
通过Tailwind设置组件的类属性、HTML属性和CSS类。只需使用属性或属性的名称作为方法名称,其参数将是值。Tailwind类可以通过class
方法应用。
Livewire组件
Livewire组件是用于创建交互式部分和完整页面的响应式组件。
全页组件应使用route
和title
方法,例如
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
,它包含了一个 h1
、input
和 p
。对于 h1
元素也是如此,它包含了一些翻译后的文本。
对于链式属性方法,只需指定一个 HTML 元素属性名,并将其值作为参数。看看上面的 Html::input()
例子,我们给它赋予了一个 type
为 email
等。
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), ),
您也可以使用 elseif
和 else
作为 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问题,我将尽快回复。感谢您查看这个包,祝您编码愉快!