{"id":21542567,"url":"https://github.com/bastinald/malzahar","last_synced_at":"2025-04-10T04:25:28.168Z","repository":{"id":50256828,"uuid":"371238221","full_name":"bastinald/malzahar","owner":"bastinald","description":"A magic PHP framework. Build reactive web apps without writing HTML, CSS, or JavaScript! Powered by Tailwind, Alpine, Laravel, \u0026 Livewire.","archived":false,"fork":false,"pushed_at":"2021-09-11T03:07:27.000Z","size":70,"stargazers_count":27,"open_issues_count":2,"forks_count":8,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-24T05:43:47.118Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bastinald.png","metadata":{"files":{"readme":"readme.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-05-27T03:44:46.000Z","updated_at":"2024-11-29T13:54:45.000Z","dependencies_parsed_at":"2022-08-25T13:20:22.928Z","dependency_job_id":null,"html_url":"https://github.com/bastinald/malzahar","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bastinald%2Fmalzahar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bastinald%2Fmalzahar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bastinald%2Fmalzahar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bastinald%2Fmalzahar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bastinald","download_url":"https://codeload.github.com/bastinald/malzahar/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248156251,"owners_count":21056808,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-24T05:10:00.030Z","updated_at":"2025-04-10T04:25:28.128Z","avatar_url":"https://github.com/bastinald.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Malzahar\n\nA magic PHP framework. Build reactive web apps without writing HTML, CSS, or JavaScript! Powered by Tailwind, Alpine, Laravel, \u0026 Livewire.\n\n### Requirements\n\n-   PHP 8\n-   Laravel 8\n-   NPM\n\n## Installation\n\nCreate a new Laravel app:\n\n```console\nlaravel new my-app\n```\n\nConfigure `.env` APP, DB, \u0026 MAIL values:\n\n```env\nAPP_*\nDB_*\nMAIL_*\n```\n\nRequire Malzahar via composer:\n\n```console\ncomposer require bastinald/malzahar\n```\n\nRun the install command:\n\n```console\nphp artisan malz:install\n```\n\nThis will install \u0026 configure Tailwind \u0026 Alpine and create some example components to get you started.\n\n## Commands\n\n### Automatic Migrations\n\n```console\nphp artisan malz:migrate {--f} {--s} {--fs}\n```\n\nThis will run the automatic migrations by comparing the `migration` methods of your models to the current database table structures and apply any necessary changes. Use the `--f` option for fresh, `--s` for seed, or `--fs` for both. Using\nautomatic migrations is completely optional with Malzahar.\n\n### Making Blade Components\n\n```console\nphp artisan malz:blade {class}\n```\n\nThis will generate a Blade abstraction component inside the `app/Components/Blade` folder. As with all generator commands, you may specify a sub folder using slashes or dot notation for the `class`.\n\n### Making Livewire Components\n\n```console\nphp artisan malz:livewire {class} {--f}\n```\n\nThis will generate a reactive Livewire component inside the `app/Components/Livewire` folder. Use the `--f` option to make a full-page component with automatic routing included.\n\n### Making Models\n\n```console\nphp artisan malz:model {class}\n```\n\nThis will generate an Eloquent model including a `migration` and `definition` method for use with the automatic migration command. It also creates a factory for the model.\n\n## Components\n\n### Blade Components\n\nBlade components are used to abstract one or more HTML components into their own PHP class so that they can be reused and maintained with ease.\n\nFor example, let's say you need to make a reusable `Button` component for your forms:\n\n```php\nnamespace App\\Components\\Blade\\Forms;\n\nuse Bastinald\\Malzahar\\Components\\Blade;\nuse Bastinald\\Malzahar\\Components\\Html;\n\nclass Button extends Blade\n{\n    public $color = 'blue';\n\n    public function attributes()\n    {\n        return [\n            'type' =\u003e 'button',\n        ];\n    }\n\n    public function classes()\n    {\n        return [\n            'text-white rounded-md shadow-sm px-4 py-2',\n            'bg-' . $color . '-600 hover:bg-' . $color . '-700',\n        ];\n    }\n\n    public function template()\n    {\n        return Html::button($this-\u003eslot)\n            -\u003emerge($this);\n    }\n}\n```\n\nThe design behind these components works similar to standard Laravel blade components, except we have a few more features. Notice the `attributes` and `classes` methods. This is where you would specify default attributes and classes for the\ncomponent. These are applied via the `merge($this)` method on the `Html::button()` component. You can also see custom properties being utilized e.g. `$color` in the example above. Properties are different from attributes. Also, notice the use of `$this-\u003eslot` in the component template. The slot is what is passed via the `make()` method parameters.\n\nNow you can use this `Button` component inside any other component via the `make()` method:\n\n```php\nclass Login extends Livewire\n{\n    public $email;\n\n    public function template()\n    {\n        return GuestLayout::make(\n            Html::form(\n                Input::make()\n                    -\u003etype('email')\n                    -\u003eplaceholder(__('Email'))\n                    -\u003eerror($this-\u003eerror('email'))\n                    -\u003ewireModelDefer('email'),\n\n                Button::make(__('Login'))\n                    -\u003ecolor('red')\n                    -\u003etype('submit')\n                    -\u003eclass('w-full'),\n            )-\u003ewireSubmitPrevent('login'),\n        );\n    }\n```\n\nNotice how we can still pass a `color`, `type` and `class` to the `Button` component via chained methods, which will be merged with whatever default properties, attributes and classes are specified inside the component class itself.\n\nChain methods on the component in order to set class properties, HTML attributes, and CSS classes via Tailwind. Just use the name of the property or attribute as the method name, and its parameter will be the value. Tailwind classes can be applied via the `class` method.\n\n### Livewire Components\n\nLivewire components are reactive components used for making interactive partial and full page experiences.\n\nFull page components should use a `route` and `title` method e.g.:\n\n```php\nclass Home extends Livewire\n{\n    public function route()\n    {\n        return Route::get('/home', static::class)\n            -\u003ename('home')\n            -\u003emiddleware('auth');\n    }\n\n    public function title()\n    {\n        return __('Home');\n    }\n\n    public function template()\n    {\n        return AuthLayout::make(\n            Html::h1($this-\u003etitle())\n                -\u003eclass('text-3xl font-bold mb-4'),\n\n            Html::p(__('You are logged in!')),\n        );\n    }\n}\n```\n\nThis full page component would be accessible via the `/home` route.\n\nFor partial components, you can create any components you want to include inside of other Livewire components, and then include them via the `make()` method. You use the `make()` method to construct all of your custom Blade and Livewire components.\n\nLet's say we created this simple partial component:\n\n```php\nclass Alert extends Livewire\n{\n    public $message;\n\n    public function mount($message)\n    {\n        $this-\u003emessage = $message;\n    }\n\n    public function template()\n    {\n        return Html::div(\n            Html::p(__($this-\u003emessage))\n                -\u003eclass('font-bold mb-4'),\n\n            Html::button(__('Change Message'))\n                -\u003eclass('bg-blue-600 text-white px-4 py-2')\n                -\u003ewireClick('changeMessage'),\n        );\n    }\n\n    public function changeMessage()\n    {\n        $this-\u003emessage = 'I am a changed message.';\n    }\n}\n```\n\nNow we can include, and even declare mounted properties for this `Alert` component in our other Livewire components via `make()`:\n\n```php\nclass Home extends Livewire\n{\n    public function template()\n    {\n        return AuthLayout::make(\n            Html::h1($this-\u003etitle())\n                -\u003eclass('text-3xl font-bold mb-4'),\n\n            Alert::make()-\u003emessage('Hello, world!'),\n        );\n    }\n```\n\nNotice how we can pass a `message` property to the `mount()` method via a magic `message()` method that is available to us dynamically. Malzahar works similarly for Blade, Livewire, and HTML components. You're a wizard, Harry!\n\nOh, and if you need to grab validation errors, you can use the `$this-\u003eerror()` method inside your Livewire component:\n\n```php\nuse App\\Components\\Blade\\Forms\\Button;\nuse App\\Components\\Blade\\Forms\\Input;\nuse App\\Components\\Blade\\Layouts\\GuestLayout;\nuse Bastinald\\Malzahar\\Components\\Html;\nuse Bastinald\\Malzahar\\Components\\Livewire;\n\nclass Login extends Livewire\n{\n    public $email, $password;\n\n    public function template()\n    {\n        return GuestLayout::make(\n            Html::form(\n                Input::make()\n                    -\u003etype('email')\n                    -\u003eplaceholder(__('Email'))\n                    -\u003eerror($this-\u003eerror('email'))\n                    -\u003ewireModelDefer('email'),\n\n                Input::make()\n                    -\u003etype('password')\n                    -\u003eplaceholder(__('Password'))\n                    -\u003eerror($this-\u003eerror('password'))\n                    -\u003ewireModelDefer('password'),\n\n                Button::make(__('Login'))\n                    -\u003etype('submit')\n                    -\u003eclass('w-full'),\n            )-\u003ewireSubmitPrevent('login'),\n        );\n    }\n\n    public function rules()\n    {\n        return [\n            'email' =\u003e ['required', 'email'],\n        ];\n    }\n\n    public function login()\n    {\n        $this-\u003evalidate();\n\n        // attempt to log the user in\n    }\n```\n\n### HTML Components\n\nHTML components are the building blocks for all of your other components. The syntax is similar to other Malzahar components, and we can add any attributes we want via magically chained methods.\n\nAll HTML components can be made by using their tag as the constructor method, and attributes for said tag come after:\n\n```php\npublic $email;\n\npublic function template()\n{\n    return Html::div(\n        Html::h1(__('Hello, world')),\n\n        Html::input()\n            -\u003etype('email')\n            -\u003eplaceholder(__('Email'))\n            -\u003ewireModelDefer('email'),\n\n        Html::p(__('Look ma, no hands!'))\n            -\u003eclass('text-blue-600 font-bold'),\n    );\n}\n```\n\nIf you notice, you'll see that the parameters for the constructing method contain the slot (or content) for that HTML element. See how the `div` in the example above contains an `h1`, `input`, and `p` inside of it. Same goes for the `h1` element, it has some translated text inside it.\n\nFor chained attribute methods, just specify an HTML element attribute name, and it's value as the parameter. Look at the `Html::input()` example above, we have given it a `type` of `email`, etc.\n\nHTML components also support Livewire and Alpine methods as well. In the example above, you can see the use of `wireModelDefer('email')`, which actually translates to `wire:model.defer=\"email\"` in the rendered HTML. This is used to bind the input value to the `$email` property when an action is performed (`defer`).\n\nSame concept goes for Alpine. Let's say we wanted to add some Tailwind transitions to a dropdown:\n\n```php\nHtml::div(\n    Html::button('Open Dropdown')\n        -\u003exOnClick('open = true'),\n\n    Html::div('Dropdown Body')\n        -\u003exShow('open')\n        -\u003exTransitionEnter('transition ease-out duration-100')\n        -\u003exTransitionEnterStart('transform opacity-0 scale-95')\n        -\u003exTransitionEnterEnd('transform opacity-100 scale-100')\n        -\u003exTransitionLeave('transition ease-in duration-75')\n        -\u003exTransitionLeaveStart('transform opacity-100 scale-100')\n        -\u003exTransitionLeaveEnd('transform opacity-0 scale-95')\n        -\u003exOnClickAway('open = false')\n        -\u003eclass('absolute bg-white p-3')\n)-\u003exData('{ open: false }'),\n```\n\nNotice how Livewire attributes start with `wire`, and the Alpine attributes start with `x`. Malzahar is smart enough to format these attributes to their proper syntax when being rendered to actual HTML on compile.\n\n### Dynamic Components\n\nSometimes you will need to use third party blade components in your Malzahar app. Fortunately, this package makes this very simple via the `Dynamic` class.\n\nFor example, let's say I installed the Laravel Honey package. Normally, to include this component inside one of my views, I would use something like this:\n\n```html\n\u003cx-honey recaptcha /\u003e\n```\n\nNow we can't use actual HTML with Malzahar, so what do we do? We use the `Dynamic` class with a magic constructor method:\n\n```php\nDynamic::honey(),\n```\n\nPassing attributes to the dynamic component is as simple as adding a chained method:\n\n```php\nDynamic::honey()-\u003erecaptcha(),\n```\n\nIt all works similarly to other Malzahar components. With dynamic components, the constructor method is the name of the component itself, and attributes are passed the same way you would with HTML or other components.\n\nCheck out the `NavLink` example that was created when you installed Malzahar. You can even see Dynamic components utilizing the `merge($this)` method inside a custom Blade component:\n\n```php\nclass NavLink extends Blade\n{\n    public $icon, $route, $title;\n\n    public function attributes()\n    {\n        return [\n            'name' =\u003e 'heroicon-o-' . $this-\u003eicon,\n        ];\n    }\n\n    public function classes()\n    {\n        return [\n            'w-6 h-6',\n            'text-gray-600 hover:text-black' =\u003e Route::currentRouteName() != $this-\u003eroute,\n            'text-blue-600' =\u003e Route::currentRouteName() == $this-\u003eroute,\n        ];\n    }\n\n    public function template()\n    {\n        return Html::a(\n            Dynamic::icon()-\u003emerge($this),\n        )-\u003ehref(route($this-\u003eroute))-\u003etitle($this-\u003etitle);\n    }\n}\n```\n\n## Statements\n\n### If Statement\n\nConditional `if` statements with Malzahar are easy:\n\n```php\nStatement::if($this-\u003elabel,\n    fn() =\u003e Html::label($this-\u003elabel),\n),\n```\n\nYou can also use `elseif` and `else` as chained methods for your `if` statement:\n\n```php\npublic $color = 'blue';\n\npublic function template()\n{\n    return Statement::if($this-\u003ecolor == 'red',\n        fn() =\u003e Html::h1(__('The color is red!'))\n            -\u003eclass('text-red-600'),\n    )-\u003eelseif($this-\u003ecolor == 'blue',\n        fn() =\u003e Html::h2(__('The color is blue!'))\n            -\u003eclass('text-blue-600'),\n    )-\u003eelse(\n        fn() =\u003e Html::h3(__('The color is something else!')),\n    );\n}\n```\n\nNotice how the first parameter of `if` is the condition, and every closure after is what will be rendered if the statement passes.\n\n### Each Statement\n\n`Each` statements, or loops as they're often called, allow you to iterate through a result set and display things per iteration.\n\nFor example, let's say we want to spit out a list of our users names:\n\n```php\nStatement::each(User::all(),\n    fn(User $user) =\u003e Html::div(e($user-\u003ename)),\n)-\u003eempty(\n    fn() =\u003e Html::p('No users found.'),\n),\n```\n\nThe first parameter of the `each` statement is the collection or array. The second parameter is a callable function with the item, and optionally the key. You can use the `empty` method to show something if no results are found.\n\nAlso, notice how the `e` function is used here to escape the users name. Normally, you'd use the `{{ $user-\u003ename }}` syntax in a view file, which literally just calls the `e` function when compiled.\n\nNeed to use the keys of the items? No problem:\n\n```php\nStatement::each(['red' =\u003e '#ff0000', 'green' =\u003e '#00ff00'],\n    fn($hexCode, $colorName) =\u003e Html::dl(\n        Html::dt($colorName),\n        Html::dd($hexCode),\n    )-\u003eclass('mb-4'),\n),\n```\n\nIf you have questions or need help, please use the Github issues and I will respond ASAP. Thank you for checking out this package and happy coding!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbastinald%2Fmalzahar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbastinald%2Fmalzahar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbastinald%2Fmalzahar/lists"}