NativePHP 教程:构建 Mac 菜单栏应用程序

发布日期 作者:

NativePHP Tutorial: Building a Mac MenuBar application image

NativePHP,由 Marcel PociotBeyondCode 创建,使 Laravel 开发人员能够利用我们已有的所有 Laravel 知识来构建原生 Mac、Windows 和 Linux 应用程序。

我最近看到 Christopher Rumpel一个应用程序 上工作,该应用程序允许您存储朋友的时区,以便您可以一目了然地查看他们的时间。

跟我一起构建一个 Mac 菜单栏应用程序,以便了解团队中每个成员的本地时间。

等等,NativePHP 到底是如何工作的?

NativePHP 允许您从两种流行的技术中选择一种在后台使用:Electron 和 Tauri。这两种技术都允许您“使用 JavaScript、HTML 和 CSS 构建跨平台的桌面应用程序”。如果您仔细想想,这有点像魔法——使用 Web 技术构建“原生”应用程序。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 首页,hello there!

给我看看代码!

当然,但请稍安勿躁,所有内容很快就会揭晓。导航到 `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())
];
}

然后将我的 `App\Database\seeders\DatabaseSeeder.php` 更新为

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

并运行 `php artisan migrate` 和 `php artisan db:seed`。

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.

让我们创建 Livewire 类和视图

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

然后将我们的 `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');

并在 `resources/views/components/layouts` 内创建一个 `app.blade.php`,其中包含以下 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>

总结

现在应用程序已按预期运行并呈现,让我们在构建它之前再做几件事。首先,让我们更新菜单栏图标。我创建了 2 张图像,一张是 22x22 png,另一张是 44x44 png。通过在这些文件的名称后添加单词 `Template`,我们可以获得一些不错的功能。在 Mac 上,NativePHP 会将这些图像转换为具有透明度的白色图标,以便它与原生菜单栏的配色方案相匹配。

这两张图片名为

menuBarIconTemplate.png

通过将这些图标添加到 `storage/app` 目录,然后将我们的 `NativeAppServiceProvider` 启动方法更新为

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

构建您的 NativePHP 应用程序

php artisan native:build

运行此命令将打包我们本地构建应用程序所需的所有内容,并为我们提供一个本地文件(“.dmg”、“.exe”等)。完成后,这些文件将被放置在项目的 `root/dist` 目录中,您可以根据需要分发该应用程序。

截至撰写本文时,`php artisan native:build` 函数有效,但是当我本地打开“.dmg”时,它会“挂起”,并且我的菜单栏应用程序不会启动。同样,NativePHP 目前仍处于 `alpha` 状态,因此可能会出现问题,BeyondCode 团队正在努力解决此类问题,我们预计在未来几周或几个月内将实现完全功能。

总结

好吧,您怎么看?用 Laravel 构建原生应用程序真是太棒了,对吧?我可以想到很多此类功能的用例,我迫不及待地想继续探索,并看到 Laravel 迈向新的高度。在 NativePHP 文档 中还有很多其他内容,这个应用程序没有介绍或讲解,您不妨自己看看,获得灵感,构建一些很棒的东西。 #laravelforever

Shane D Rosenthal photo

我是一个技术狂,家庭成员,社区领袖,飞行员和音乐家。从 80 年代中期开始,我就一直拆卸东西来了解它们的工作原理,并试图重新组装它们,有时比以前更好。在此过程中,我遇到了我生命中的挚爱,组建了一个家庭,在领导和教育他人方面找到了目标,最近还成为了一名飞行员。我对飞行充满热情,喜欢与周围的人分享世界,并在可能的情况下观看现场金属音乐演出。

展望未来,我打算拥有自己的飞机,保持仪表等级,继续教育和指导我们的年轻人,发展我的 YouTube 频道和粉丝,并分享我的热情,以影响我所能影响的任何人。

Cube

Laravel 新闻快讯

加入 40,000 多名其他开发者,不错过任何新的提示、教程等。

Laravel Forge logo

Laravel Forge

轻松创建和管理您的服务器,并在几秒钟内部署您的 Laravel 应用程序。

Laravel Forge
Tinkerwell logo

Tinkerwell

Laravel 开发人员必备的代码运行器。使用 AI、自动完成和本地和生产环境的即时反馈进行调试。

Tinkerwell
No Compromises logo

没有妥协

Joel 和 Aaron,来自 No Compromises 播客的两名经验丰富的开发人员,现在可以为您的 Laravel 项目雇用。 ⬧ 固定费用 7500 美元/月。 ⬧ 没有冗长的销售流程。 ⬧ 没有合同。 ⬧ 100% 退款保证。

没有妥协
Kirschbaum logo

Kirschbaum

提供创新和稳定性,确保您的 Web 应用程序成功。

Kirschbaum
Shift logo

Shift

正在运行旧版本的 Laravel?即时、自动化的 Laravel 升级和代码现代化,让您的应用程序保持新鲜。

Shift
Bacancy logo

Bacancy

只需 2500 美元/月,即可获得经验丰富的 Laravel 开发人员(拥有 4-6 年的经验)为您的项目增光添彩。获得 160 小时的专业知识和 15 天的无风险试用。立即安排电话会议!

Bacancy
Lucky Media logo

Lucky Media

现在就选择 Lucky - Laravel 开发的理想之选,拥有超过十年的经验!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar:Laravel 电子商务

Laravel 的电子商务。一个开源软件包,将现代无头电子商务功能的强大功能带入 Laravel。

Lunar:Laravel 电子商务
LaraJobs logo

LaraJobs

官方 Laravel 招聘网站

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit:Laravel SaaS 启动工具包

SaaSykit 是一个 Laravel SaaS 启动工具包,它包含运行现代 SaaS 所需的所有功能。支付、漂亮的结账、管理面板、用户仪表板、身份验证、现成的组件、统计信息、博客、文档等等。

SaaSykit:Laravel SaaS 启动工具包
Rector logo

Rector

您无缝升级 Laravel、降低成本和加速创新以帮助成功公司发展的合作伙伴

Rector
MongoDB logo

MongoDB

通过 MongoDB 和 Laravel 的强大集成增强您的 PHP 应用程序,使开发人员能够轻松高效地构建应用程序。在使用熟悉的 Eloquent API 的同时,支持事务性、搜索、分析和移动用例。了解 MongoDB 的灵活、现代数据库如何改变您的 Laravel 应用程序。

MongoDB
Maska is a Simple Zero-dependency Input Mask Library image

Maska 是一个简单的零依赖输入掩码库

阅读文章
Add Swagger UI to Your Laravel Application image

将 Swagger UI 添加到您的 Laravel 应用程序

阅读文章
Assert the Exact JSON Structure of a Response in Laravel 11.19 image

在 Laravel 11.19 中断言响应的精确 JSON 结构

阅读文章
Build SSH Apps with PHP and Laravel Prompts image

使用 PHP 和 Laravel Prompts 构建 SSH 应用程序

阅读文章
Building fast, fuzzy site search with Laravel and Typesense image

使用 Laravel 和 Typesense 构建快速、模糊的网站搜索

阅读文章
Add Comments to your Laravel Application with the Commenter Package image

使用 Commenter 软件包将评论添加到您的 Laravel 应用程序

阅读文章