• Час читання ~7 хв
  • 04.08.2023

NativePHP, створений Марселем Поціотом у BeyondCode, дозволяє нам розробникам Laravel використовувати ВСІ робочі знання, які ми вже маємо з Laravel, для створення рідних програм Mac, Windows та Linux.

Нещодавно я бачив, як Крістофер Румпель працює над додатком, який дозволяє зберігати часові пояси ваших друзів, щоб ви могли з першого погляду побачити, котра година.

Слідкуйте разом зі мною, коли ми створюємо програму Mac MenuBar, щоб знати місцевий час кожного члена вашої команди.

Зачекайте - як взагалі працює NativePHP??

NativePHP дозволяє вибирати з двох різних популярних технологій для використання під капотом, Electron і Tauri. Обидва вони дозволяють "Створювати кросплатформні настільні програми за допомогою JavaScript, HTML та CSS". Це схоже на чаклунство, якщо задуматися - веб-технології для створення "рідного" додатка. NativePHP надає простий API зі знайомим (Laravel) способом створення додатків в будь-якій з цих базових технологій. Для цього прикладу я буду демонструвати обгортку Electron.

Встановлення NativePHP та Hello World

У свіжому додатку Laravel:Почнемо з встановлення пакета:Запустіть інсталятор:Я хочу, щоб ви вручну запустили програму, щоб звикнути робити це таким чином:

laravel new team-time

composer require nativephp/electron

php artisan native:install
Would you like to install the NativePHP NPM dependencies? - Select 'yes'
Would you like to start the NativePHP development server? - Select 'no'

Через мить ви повинні побачити, як розкручується рідна настільна програма

php artisan native:serve

, відображаючи домашню сторінку Laravel за замовчуванням, привіт!

Покажіть мені код!

Звичайно, але трохи заспокойтеся, все розкриється найближчим часом. Перейдіть до розділу App\Providers\NativeAppServiceProvider.php. Тут ви можете побачити деякі з API NativePHP, розроблені для вас. Однак для цього прикладу ми не збираємося використовувати цей код. Ідіть вперед і очистіть все в методі boot та замініть його наступним:

<?php
namespace App\Providers;

use Native\Laravel\Facades\MenuBar;

class NativeAppServiceProvider
{
    public function boot(): void
    {
        Menubar::create();
    }
}

Оскільки NativePHP робить гаряче перезавантаження, ми повинні побачити Window закриття та піктограму Menubar у верхній частині вашого комп'ютера. Натискання на нього відкриє ту саму домашню сторінку Laravel за замовчуванням.

Гарний! Давайте побудуємо щось круте!

За лаштунками я встановлюю TailwindCSS за їхніми документами, Laravel Livewire 3 (кубик, я знаю, але це мій вибір наркотику), Blade Heroicons, а потім додаю нашу модель TeamMember, міграцію та фабрику за допомогою такої команди:Міграція:Фабрика

php artisan make:model TeamMember -mf
NOTE: I am keeping `npm run dev` running for hot reloading of the ui.

Потім оновлюю мій App\Database\seeders\DatabaseSeeder.php до:

public function up(): void
{
    Schema::create('team_members', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->string('timezone');
        $table->timestamps();
    });
}

public function definition(): array
{
    return [
        'name' => $this->faker->name,
        'timezone' => $this->faker->randomElement(timezone_identifiers_list())
    ];
}

public function run(): void
{
     \App\Models\TeamMember::factory(10)->create();
}

І запущено php artisan migrate та .

NOTE: The application inside of NativePHP does NOT have access to the database defined in your `.env`. From my experience, it can be useful to seed your database locally and debug in the browser or by using Spatie/Ray.

php artisan db:seedДавайте створимо наші класи та подання

php artisan livewire:make TeamMember/Index
php artisan livewire:make TeamMember/Create
php artisan livewire:make TeamMember/Update

Livewire Потім оновіть наш web.php до наступного:

Route::get('/', \App\Livewire\TeamMember\Index::class)->name('index');
Route::get('/team-members/create', \App\Livewire\TeamMember\Create::class)->name('create');
Route::get('/team-members/{teamMember}/edit', \App\Livewire\TeamMember\Update::class)->name('edit');

І створіть app.blade.php внутрішню частину resources/views/components/layouts за допомогою наступного html

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Laravel</title>
    @vite('resources/css/app.css')
</head>
<body class="antialiased bg-gray-900 text-gray-100">
<div class="max-w-md mx-auto px-4 py-6">
    {{$slot}}
</div>
</body>
</html>

: Список наших товаришів

по команді Усередині App\Livewire\TeamMember\Index класу нам потрібно отримати всіх членів команди, щоб відобразити їх, крім того, ми повинні запропонувати посилання для створення нового члена команди та запропонувати кнопки оновлення та видалення для існуючих членів команди.

Клас:Перегляд:Якщо ви посіяли свою базу даних локально, то попередній перегляд цього в браузері має виглядати так:

<?php
namespace App\Livewire\TeamMember;

use App\Models\TeamMember;
use Livewire\Component;

class Index extends Component
{
    public function deleteMember(TeamMember $member)
    {
        $member->delete();
    }
    public function render()
    {
        $team = TeamMember::get();
        return view('livewire.team-member.index', compact('team'));
    }
}

<div>
    <div class="flex items-center justify-between mb-10">
        <h1 class="text-xl font-bold">My Team</h1>
        <a href="{{route('create')}}" type="button"
           class="rounded-full bg-pink-600 px-2 py-1 text-xs font-bold text-white shadow hover:bg-pink-500">Add Team
            Mate</a>
    </div>
    <div wire:poll>
        @foreach($team as $member)
        <div wire:key="{{ $member->id }}" class="my-2 flex items-center justify-between">
            <div>
                <p class="text-xs font-bold text-sky-500">{{$member->name}}</p>
                <p class="text-lg">{{now()->tz($member->timezone)->format('h:i:s A')}} <span
                    class="text-xs text-gray-500">- {{$member->timezone}}</span></p>
            </div>
            <div class="flex items-center">
                <a href="{{route('edit', ['team-member' => $member])}}">
                    <span class="sr-only">Edit</span>
                    <x-heroicon-m-pencil class="w-5 h-5 mr-3 hover:text-pink-500 transition-all duration-300" />
                </a>
                <button wire:click="deleteMember({{$member}})">
                    <x-heroicon-m-trash class="w-5 h-5 mr-3 hover:text-red-600 transition-all duration-300" />
                </button>
            </div>
        </div>
        @endforeach
    </div>
</div>

У нативному додатку це має виглядати так,

оскільки у нас ще немає даних (обов'язково запустіть npm run build тодіphp artisan native:serve). NativePHP використовує локальну базу даних SQLite за лаштунками, нам не потрібні додаткові налаштування або конфігурація для неї.

Тепер давайте впораємося з операціямиCreate, щоб ми могли побачити це і в рідному додатку.

Клас:Перегляд:Якщо ви посіяли свою базу даних локально, то попередній перегляд цього в браузері має виглядати так:

<?php
namespace App\Livewire\TeamMember;

use App\Models\TeamMember;
use Livewire\Attributes\Rule;
use Livewire\Component;

class Create extends Component
{
    #[Rule(['required', 'string', 'min:3'])]
    public string $name;

    #[Rule(['required', 'string', 'min:3'])]
    public string $timezone;

    public function createMember()
    {
        TeamMember::create($this->validate());
        $this->redirectRoute('index');
    }
    public function render()
    {
        return view('livewire.team-member.create');
    }
}

<div>
    <div class="flex items-center justify-between mb-10">
        <h1 class="text-xl font-bold">Add Team Member</h1>
        <a href="{{route('index')}}" type="button"
           class="rounded-full bg-pink-600 px-2 py-1 text-xs font-bold text-white shadow hover:bg-pink-500 flex items-center">
            Go Back
        </a>
    </div>
    <form wire:submit="createMember">
        <div>
            <label for="name" class="block text-sm font-medium leading-6 text-gray-100">What is your team member's
                name?</label>
            <div class="mt-2">
                <input type="text" wire:model="name" id="name"
                       class="block w-full rounded-md border-0 py-1.5 text-gray-400 shadow-sm bg-gray-800 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-pink-600 sm:text-sm sm:leading-6"
                       placeholder="Sarthak">
                @error('name')
                <div class="mt-1 text-red-500 text-sm">{{ $message }}</div>
                @enderror
            </div>
        </div>

        <div class="mt-6">
            <label for="timezone" class="block text-sm font-medium leading-6 text-gray-100">What is your team member's
                timezone</label>
            <select id="timezone" wire:model="timezone"
                    class="mt-2 block w-full rounded-md border-0 py-1.5 text-gray-400 shadow-sm bg-gray-800 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-pink-600 sm:text-sm sm:leading-6">
                @foreach(timezone_identifiers_list() as $timezone)
                <option wire:key="{{ $timezone }}">{{$timezone}}</option>
                @endforeach
            </select>
            @error('timezone')
            <div class="mt-1 text-red-500 text-sm">{{ $message }}</div>
            @enderror
        </div>
        <button type="submit"
                class="mt-6 rounded bg-pink-600 px-2 py-1 font-bold text-white shadow hover:bg-pink-500 w-full">Add Team
            Mate
        </button>
    </form>
</div>

Тепер ми готуємо'! Але, схоже, я поставив Sarthak не в той часовий пояс, давайте налаштуємо наш клас Edit і переглянемо і присипимо цього цуценя.

Клас:Перегляд:Якщо ви посіяли свою базу даних локально, то попередній перегляд цього в браузері має виглядати так:

<?php
namespace App\Livewire\TeamMember;

use App\Models\TeamMember;
use Livewire\Component;
use Livewire\Features\SupportValidation\Rule;

class Update extends Component
{
    public TeamMember $teamMember;

    #[Rule(['required','min:3', 'string'])]
    public $name;

    #[Rule(['required','string'])]
    public $timezone;

    public function mount(TeamMember $teamMember)
    {
        $this->teamMember = $teamMember;
        $this->name = $teamMember->name;
        $this->timezone = $teamMember->timezone;
    }
    public function saveMember()
    {
        $this->teamMember->update([
            'name' => $this->name,
            'timezone' => $this->timezone
        ]);
        $this->redirectRoute('index');
    }
    public function render()
    {
        return view('livewire.team-member.update');
    }
}

<div>
    <div class="flex items-center justify-between mb-10">
        <h1 class="text-xl font-bold">Update Team Member</h1>
        <a href="{{route('index')}}" type="button"
           class="rounded-full bg-pink-600 px-2 py-1 text-xs font-bold text-white shadow hover:bg-pink-500 flex items-center">
            Go Back
        </a>
    </div>
    <form wire:submit="saveMember">
        <div>
            <label for="name" class="block text-sm font-medium leading-6 text-gray-100">Name</label>
            <div class="mt-2">
                <input type="text" wire:model.blur="name" id="name"
                       class="block w-full rounded-md border-0 py-1.5 text-gray-200 shadow-sm bg-gray-800 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-pink-600 sm:text-sm sm:leading-6"
                       placeholder="Sarthak">
                @error('name')
                <div class="mt-1 text-red-500 text-sm">{{ $message }}</div>
                @enderror
            </div>
        </div>

        <div class="mt-6">
            <label for="timezone" class="block text-sm font-medium leading-6 text-gray-100">Timezone</label>
            <select id="timezone" wire:model="timezone"
                    class="mt-2 block w-full rounded-md border-0 py-1.5 text-gray-200 shadow-sm bg-gray-800 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-pink-600 sm:text-sm sm:leading-6">
                @foreach(timezone_identifiers_list() as $timezone)
                <option {{$teamMember->timezone === $timezone ? 'selected' : ''}}>{{$timezone}}</option>
                @endforeach
            </select>
            @error('timezone')
            <div class="mt-1 text-red-500 text-sm">{{ $message }}</div>
            @enderror
        </div>
        <button type="submit"
                class="mt-6 rounded bg-pink-600 px-2 py-1 font-bold text-white shadow hover:bg-pink-500 w-full">Add Team
            Mate
        </button>
    </form>
</div>

Wrapping It Up Тепер

, коли додаток працює і виглядає так, як ми хочемо, давайте зробимо ще кілька речей, перш ніж створювати його. Спочатку давайте оновимо іконки MenuBar. Я створив 2 зображення, одне - 22x22 png, а інше - 44x44 png. Суфіксуючи назву цих файлів словомTemplate, ми отримуємо приємну функціональність. На Mac NativePHP перетворить ці зображення в білий значок з прозорістю, щоб вони відповідали колірній схемі рідного рядка меню.

Два зображення названі:Додавши ці піктограми до каталогуstorage/app, а потім оновивши наш NativeAppServiceProvider метод завантаження до:

menuBarIconTemplate.png
[email protected]

public function boot(): void
{
    Menubar::create()->icon(storage_path('app/menuBarIconTemplate.png'));;
}

На наступній подачі ми повинні побачити оновлення піктограми в нашому рядку меню.

Нарешті, давайте додамо деякі елементи до нашого .env файлу, щоб повідомити NativePHP деякі подробиці про наш додаток:

NATIVEPHP_APP_NAME="TeamTime"
NATIVEPHP_APP_VERSION="1.0.0"
NATIVEPHP_APP_ID="com.teamtime.desktop"
NATIVEPHP_DEEPLINK_SCHEME="teamtime"
NATIVEPHP_APP_AUTHOR="Shane D Rosenthal"
NATIVEPHP_UPDATER_ENABLED=false

Створіть свій додаток

php artisan native:build

NativePHP Запуск цієї команди упакує все, що нам потрібно для локального створення програми, і надасть нам власний файл (".dmg", ".exe" тощо). Після завершення файли будуть розміщені в каталозі вашого проектуroot/dist, і ви зможете розповсюджувати додаток так, як вважаєте за потрібне.

На момент написання цієї статті функція php artisan native:build працює, однак, коли я відкриваю .dmg локально, вона як би "зависає", і моя програма в рядку меню не запускається. Знову ж таки, NativePHP все ще перебуває в стані, і очікуються проблеми, команда BeyondCode наполегливо працює над виправленням подібних елементів, і ми повинні очікувати повної функціональності в alpha найближчі тижні або місяці.

Резюме

Ну, що ви думали? Досить чудово, що ми можемо створювати нативні програми за допомогою Laravel, чи не так? Я можу придумати багато варіантів використання такої функції, і я не можу дочекатися, щоб продовжити дослідження та побачити, як Laravel підштовхується до нових висот. У документах NativePHP є так багато інших елементів, які ця програма не охоплює і не переглядає, подивіться самі, надихніться та створіть щось приголомшливе. #laravelforever!

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Про автора CrazyBoy49z
WORK EXPERIENCE
Контакти
Ukraine, Lutsk
+380979856297