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!