首頁 >後端開發 >php教程 >使用 Flight 建立簡單的部落格 - 第 1 部分

使用 Flight 建立簡單的部落格 - 第 1 部分

PHPz
PHPz原創
2024-07-18 01:21:411224瀏覽

大家好!我認為是時候展示 PHP 飛行框架中添加的一些新功能了。今年早些時候,Flight Mike Cao 的原始創建者慷慨地提出將 mikecao/flight 的所有權轉移給一個新的 Flight PHP 組織。自從它被移動以來,我們添加了中間件、路由分組、DIC 和其他功能等功能。這篇文章會有點長,但這只是因為我包含了很多程式碼範例,因此您可以了解如何建立部落格的正確上下文。

首先,讓我們解決這個問題。 Flight 是一個帶有一些花哨功能的簡單框架。它不會與 Laravel 或 Symfony 或 Yii 或 Cake 或 [填空] 競爭。該框架實際上是針對簡單到中型專案而建造的。它也迎合了那些不喜歡程式碼中難以理解或難以訓練的“魔法”的人。它更適合那些剛開始涉足框架的開發人員,而不是帶有大量隨機包含語句的原始 PHP。

太長了;博士

很多很酷的功能,很好的簡單實現,等等等等,這是程式碼。請前往第 2 部分了解精彩內容!

安裝

讓我們使用 Composer 來開始這個聚會。

composer create-project flightphp/skeleton blog/
cd blog/

配置您的新項目

要做的第一件事是轉到 app/config/config.php 文件,我們可以在其中放置任何配置,例如 API 金鑰、資料庫憑證和應用程式的其他重要憑證。對於本博客,我們將取消 SQLite 資料庫路徑的 file_path 行的註解:

return [
    'database' => [
        // 'host' => 'localhost',
        // 'dbname' => 'dbname',
        // 'user' => 'user',
        // 'password' => 'password'
        'file_path' => __DIR__ . $ds . '..' . $ds . 'database.sqlite'
    ],
];

建立部落格資料庫

Flight 現在附帶一個名為 runway 的命令列實用程式。這允許您為 Flight 插件甚至您自己的專案建立自訂命令。

作為骨架的一部分,它附帶了一個 SampleDatabaseCommand,它將為我們正在創建的這個部落格專案提供一個起點。

執行以下命令,它應該會為您填充您的資料庫!

php runway init:sample-db

接下來我們將開啟 app/config/services.php 檔案並取消註解 SQLite 行。

// see how the $config variable references the config line we uncommented earlier?
$dsn = 'sqlite:' . $config['database']['file_path'];

為了確保我們已正確設定所有內容,請執行composer start,然後在瀏覽器中造訪http://localhost:8000/。您應該看到以下畫面:

Default Home Page

您還會注意到角落裡有一個方便的調試工具欄,其中包含一些自訂的 Flight 面板,可幫助您了解應用程式中發生的情況。如果您將滑鼠懸停在工具列中的各個項目上,您將看到各種懸停,您可以單擊這些懸停以保持頁面上的黏性(稍後會詳細介紹)。

Flight Tracy Extensions

建立 HTML 模板

Flight 框架中確實附帶了一個非常基本的 HTML 模板解決方案。這對於非常簡單的網站或只是返回一段簡單的 HTML 來說就很好了。建議使用其他模板平台,例如 Latte、Twig 或 Blade。在本教程中,我們將使用 Latte,因為它很棒並且沒有依賴項(您會注意到在 Flight 中我們不喜歡不必要的依賴項)!

繼續安裝 Latte

composer require latte/latte

將其新增至您的 services.php

$Latte = new \Latte\Engine;
$Latte->setTempDirectory(__DIR__ . '/../cache/');

// This is fun feature of Flight. You can remap some built in functions with the framework
// to your liking. In this case, we're remapping the Flight::render() method.
$app->map('render', function(string $templatePath, array $data = [], ?string $block = null) use ($app, $Latte) {
    $templatePath = __DIR__ . '/../views/'. $templatePath;
    $Latte->render($templatePath, $data, $block);
});

現在我們已經有了模板引擎,我們可以建立一個基本 HTML 檔案。讓我們建立一個layout.latte 檔案:

<!doctype html>
<html lang="en">
    <head>
        <!-- Picnic.css is a CSS framework that works out of the box with little configuration -->
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/picnic">
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>{$page_title ? $page_title.' - '}Blog Built with Flight!</title>
    </head>
    <body style="padding: 15px;">
        {block content}{/block}
    </body>
</html>

Active Record 資料庫類

Flight 有一個用於與名為 Flight Active Record 的資料庫進行互動的插件。該外掛程式可以幫助您不必在應用程式中編寫太多原始 SQL(儘管有時編寫原始 SQL 查詢比強制活動記錄/ORM/映射器為您運行它更有效)。基本上,活動記錄擴充功能可以幫助您與資料庫中表格中的行進行互動:資料庫中的一行可以對應到 PHP 中的一個物件(自動完成列),從而節省時間和理智。讓我們將其安裝到我們的專案中。

composer require flightphp/active-record

現在您可以使用 runway 自動為您建立活動記錄類,並且它將自動建立您的屬性作為註釋(用於自動完成)!

首先讓我們建立 posts 類別。第一次執行此程式時,需要設定資料庫連線。

$ php runway make:record posts
Database configuration not found. Please provide the following details:
Driver (mysql/pgsql/sqlite): sqlite
Database file path [database.sqlite]: app/database.sqlite
Username (for no username, press enter) []: 
Password (for no password, press enter) []: 
Writing database configuration to .runway-config.json
Creating directory app/records
Active Record successfully created at app/records/PostRecord.php

現在我們將建立評論記錄類別:

$ php runway make:record comments

It's Time for your First Page!

Flight uses the MVC pattern. In order to create a new page you need to define a route in your routes.php file, create a new method in a controller, and then create the HTML file that the browser will serve. You can use runway to help you get started with a new controller class:

php runway make:controller Home

And you should see something similar to the following:

$ php runway make:controller Home
Controller successfully created at app/controllers/HomeController.php

If you go to app/controllers/HomeController.php go ahead and add this new method to your HomeController:

/**
 * Index
 * 
 * @return void
 */
public function index(): void
{
    $this->app->render('home.latte', [ 'page_title' => 'Home' ]);
}

And create a new file in app/views/home.latte and put in this code:

{extends 'layout.latte'}

{block content}
<h1>My Home Page</h1>
<p><a href="/blog">View My Blog!</a></p>
{/block}

Finally let's change up the routes to the routes.php file. Go ahead and remove any code in the routes file that begins with $router-> and add a new route for your home router:

$router->get('/', \app\controllers\HomeController::class . '->index');

Make sure you run composer start so that your development server is up. If you go to http://localhost:8000/ in your browser, you should see something like this!

Flight Demo Home Page

Now we're cookin'!

Adding Routes for the Blog

Let's go ahead and add all the methods in your controller, routes, and html files. Let's start with adding the routes in your routes.php file:

// Blog
$router->group('/blog', function(Router $router) {

    // Posts
    $router->get('', \app\controllers\PostController::class . '->index');
    $router->get('/create', \app\controllers\PostController::class . '->create');
    $router->post('', \app\controllers\PostController::class . '->store');
    $router->get('/@id', \app\controllers\PostController::class . '->show');
    $router->get('/@id/edit', \app\controllers\PostController::class . '->edit');
    $router->post('/@id/edit', \app\controllers\PostController::class . '->update');
    $router->get('/@id/delete', \app\controllers\PostController::class . '->destroy');
});

So you'll notice we use a group() method here to group all the routes together that start with /blog. We could actually rewrite the routes like the following with the group() method and the same thing would happen:

// Posts
$router->get('/blog', \app\controllers\PostController::class . '->index');
$router->get('/blog/create', \app\controllers\PostController::class . '->create');

With the controller, first let's create an empty controller with runway:

php runway make:controller Post

You can copy the code below for your PostController.php:

<?php

declare(strict_types=1);

namespace app\controllers;

use app\records\CommentRecord;
use app\records\PostRecord;
use flight\Engine;

class PostController
{
    /** @var Engine */
    protected Engine $app;

    /**
     * Constructor
     */
    public function __construct(Engine $app)
    {
        $this->app = $app;
    }

    /**
     * Index
     *
     * @return void
     */
    public function index(): void
    {
        $PostRecord = new PostRecord($this->app->db());
        $posts = $PostRecord->order('id DESC')->findAll();
        $CommentRecord = new CommentRecord($this->app->db());
        foreach($posts as &$post) {
            $post->comments = $CommentRecord->eq('post_id', $post->id)->findAll();
        }
        $this->app->render('posts/index.latte', [ 'page_title' => 'Blog', 'posts' => $posts]);
    }

    /**
     * Create
     *
     * @return void
     */
    public function create(): void
    {
        $this->app->render('posts/create.latte', [ 'page_title' => 'Create Post']);
    }

    /**
     * Store
     *
     * @return void
     */
    public function store(): void
    {
        $postData = $this->app->request()->data;
        $PostRecord = new PostRecord($this->app->db());
        $PostRecord->title = $postData->title;
        $PostRecord->content = $postData->content;
        $PostRecord->username = $postData->username;
        $PostRecord->created_at = gmdate('Y-m-d H:i:s');
        $PostRecord->updated_at = null;
        $PostRecord->save();
        $this->app->redirect('/blog');
    }

    /**
     * Show
     *
     * @param int $id The ID of the post
     * @return void
     */
    public function show(int $id): void
    {
        $PostRecord = new PostRecord($this->app->db());
        $post = $PostRecord->find($id);
        $CommentRecord = new CommentRecord($this->app->db());
        $post->comments = $CommentRecord->eq('post_id', $post->id)->findAll();
        $this->app->render('posts/show.latte', [ 'page_title' => $post->title, 'post' => $post]);
    }

    /**
     * Edit
     *
     * @param int $id The ID of the post
     * @return void
     */
    public function edit(int $id): void
    {
        $PostRecord = new PostRecord($this->app->db());
        $post = $PostRecord->find($id);
        $this->app->render('posts/edit.latte', [ 'page_title' => 'Update Post', 'post' => $post]);
    }

    /**
     * Update
     *
     * @param int $id The ID of the post
     * @return void
     */
    public function update(int $id): void
    {
        $postData = $this->app->request()->data;
        $PostRecord = new PostRecord($this->app->db());
        $PostRecord->find($id);
        $PostRecord->title = $postData->title;
        $PostRecord->content = $postData->content;
        $PostRecord->username = $postData->username;
        $PostRecord->updated_at = gmdate('Y-m-d H:i:s');
        $PostRecord->save();
        $this->app->redirect('/blog');
    }

    /**
     * Destroy
     *
     * @param int $id The ID of the post
     * @return void
     */
    public function destroy(int $id): void
    {
        $PostRecord = new PostRecord($this->app->db());
        $post = $PostRecord->find($id);
        $post->delete();
        $this->app->redirect('/blog');
    }
}

Let's kill some time and talk about a few things that are going on in the controller.

First off we are now using our new active record classes:

$PostRecord = new PostRecord($this->app->db());
$posts = $PostRecord->order('id DESC')->findAll();

We are injecting the database we setup in the services.php file above with $this->app->db();. Technically we could also just use Flight::db() as this points to the global $app variable.

Active Record classes are really helpful to simplify interactions with a database. We could rewrite the above in the following code:

$posts = $this->app->db()->fetchAll("SELECT * FROM posts ORDER BY id DESC");

This might not be the best example of how helpful an active record could be. But in part 2 I'll show you some hidden gems inside these classes that make it so much better than writing raw SQL.

Now let's talk HTML files. Here are the files we'll need for the post routes:

app/views/posts/index.latte

{extends '../layout.latte'}

{block content}

<h1>My Amazing Blog</h1>
<p>Welcome to my blog!</p>
<p><a class="button" href="/blog/create">Create a new post</a></p>
{foreach $posts as $post}
    {first}
    <h2>Recent Posts</h2>
    {/first}
    <hr>
    <h3><a href="/blog/{$post->id}">{$post->title}</a></h3>
    <p><small>By: {$post->username} on {$post->created_at|date:'d.m.Y G:i a'}</small></p>
    <p>Comments: {count($post->comments)}
    <p>{$post->content|truncate:100}</p>
    <hr>
    <a class="pseudo button" href="/blog/{$post->id}/edit">Update</a> - <a class="pseudo button" href="/blog/{$post->id}/delete">Delete</a>
{/foreach}

{/block}

app/views/posts/show.latte

{extends '../layout.latte'}

{block content}
<a href="/blog">&lt; Back to blog</a>
<h1>{$post->title}</h1>
<p>Created by: {$post->username} on {$post->created_at|date:'d.m.Y G:i a'}.</p>
<div>
    {$post->content|breakLines}
</div>
<p n:if="$post->update_at">Last update: {$post->update_at|date:'d.m.Y G:i a'}.</p>

<h2>Comments</h2>
{foreach $post->comments as $comment}
<div>
    <p>{$comment->username} on {$comment->created_at|date:'d.m.Y G:i a'}.</p>
    <div>
        {$comment->content|breakLines}
    </div>
    <hr>
    <a class="pseudo button" href="/blog/{$post->id}/comment/{$comment->id}/delete">Delete</a>
</div>
{else}
<p>No comments yet.</p>
{/foreach}

<h2>Add comment</h2>
<form action="/blog/{$post->id}/comment" method="post">
    <div>
        <label for="username">Username:</label>
        <input name="username" id="username" placeholder="Username" required />
    </div>
    <div>
        <label for="content">Comment:</label>
        <textarea name="content" id="content" placeholder="Comment" required></textarea>
    </div>
    <div>
        <button type="submit">Add Comment</button>
    </div>
</form>
{/block}

app/views/posts/create.latte

{extends '../layout.latte'}

{block content}
<h1>Create a Post</h1>
<form action="/blog" method="post">
    <label><input type="text" name="title" placeholder="Title" required></label>
    <label><textarea name="content" placeholder="Content" required></textarea></label>
    <label><input type="text" name="username" placeholder="Username" required></label>

    <button type="submit">Create</button>
</form>
{/block}

app/views/posts/edit.latte

{extends '../layout.latte'}

{block content}
<h1>Update a Post</h1>
<form action="/blog/{$post->id}/edit" method="post">
    <label for="title">Title</label>
    <input type="text" name="title" placeholder="Title" value="{$post->title}" required>

    <label for="content">Content</label>
    <label><textarea name="content" placeholder="Content" required>{$post->content}</textarea>

    <label for="username">Username</label>
    <label><input type="text" name="username" placeholder="Username" value="{$post->username}" required>

    <button type="submit">Update</button>
</form>
{/block}

Create a new post

Now that we've got all the pieces in place, you should be able to load up your blog page, create a new post, see a post, and delete a post. You may have noticed we've included a comment form but the form doesn't actually work. We can fix that real quick! Let's create a controller with runway:

php runway make:controller Comment

Now you can make the CommentController.php look like the following:

<?php

declare(strict_types=1);

namespace app\controllers;

use app\records\CommentRecord;
use flight\Engine;

class CommentController
{
    /** @var Engine */
    protected Engine $app;

    /**
     * Constructor
     */
    public function __construct(Engine $app)
    {
        $this->app = $app;
    }

    /**
     * Store
     * 
     * @param int $id The post ID
     * 
     * @return void
     */
    public function store(int $id): void
    {
        $postData = $this->app->request()->data;
        $CommentRecord = new CommentRecord($this->app->db());
        $CommentRecord->post_id = $id;
        $CommentRecord->username = $postData->username;
        $CommentRecord->content = $postData->content;
        $CommentRecord->created_at = gmdate('Y-m-d H:i:s');
        $CommentRecord->updated_at = null;
        $CommentRecord->save();
        $this->app->redirect('/blog/' . $id);
    }

    /**
     * Destroy
     * 
     * @param int $id The post ID
     * @param int $comment_id The comment ID
     * 
     * @return void
     */
    public function destroy(int $id, int $comment_id): void
    {
        $CommentRecord = new CommentRecord($this->app->db());
        $CommentRecord->find($comment_id);
        $CommentRecord->delete();
        $this->app->redirect('/blog/' . $id);
    }

}

Now let's add a couple other routes in the group chunk of code in routes.php

// Blog
$router->group('/blog', function(Router $router) {

    // Posts

    // post routes...

    // Comments
    $router->post('/@id/comment', \app\controllers\CommentController::class . '->store');
    $router->get('/@id/comment/@comment_id/delete', \app\controllers\CommentController::class . '->destroy');
});

Conclusion (sort of)

With these two additions to the code, you have a fully functioning blog built with Flight! This got the job done and you now have a blog, but the code is somewhat clunky and could be improved to have some pretty nifty features like middleware, permissions, and writing less code! Hop over to part 2

Go ahead and leave any questions in comments below or join us in the chatroom!

If you want to see the final product with all the improvements here's the code!

以上是使用 Flight 建立簡單的部落格 - 第 1 部分的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn