• Время чтения ~8 мин
  • 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'

Через некоторое время вы должны увидеть собственное настольное приложение, отображающее домашнюю страницу Laravel по умолчанию,

php artisan native:serve

здравствуйте!

Покажите мне код!

Конечно, но успокойтесь немного, все будет раскрыто в ближайшее время. Перейдите к .App\Providers\NativeAppServiceProvider.php Здесь вы можете увидеть некоторые из NativePHP API, заглушенные для вас. Однако в этом примере мы не собираемся использовать этот код. Идите вперед и очистите все в методе 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.

Затем обновляю мой до:

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 App\Database\seeders\DatabaseSeeder.php и .

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Давайте создадим наши классы и представления Livewire Затем

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

обновите наш web.php до следующего:И создайте app.blade.php внутреннюю часть resources/views/components/layouts со следующим html :

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');

<!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>

Теперь мы готовим! Но, похоже, я установил Сартака на неправильный часовой пояс, давайте настроим наш класс редактирования, посмотрим и уложим этого щенка спать.

Класс:Вид:В нативном приложении это должно выглядеть так,Если вы заполнили базу данных локально, то предварительный просмотр этого в браузере должен выглядеть следующим образом:

<?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