>  기사  >  백엔드 개발  >  Flight로 간단한 블로그 만들기 - 1부

Flight로 간단한 블로그 만들기 - 1부

PHPz
PHPz원래의
2024-07-18 01:21:411183검색

안녕하세요 여러분! 이제 PHP용 Flight Framework에 추가된 새로운 기능 중 일부를 선보일 때가 되었다고 생각했습니다. 올해 초 Flight의 최초 창시자인 Mike Cao는 mikecao/flight의 소유권을 새로운 Flight PHP 조직으로 이전하겠다고 기꺼이 제안했습니다. 이동한 이후 미들웨어, 경로 그룹화, DIC 및 기타 기능과 같은 기능을 추가했습니다. 이 게시물은 좀 더 길어지지만 블로그가 어떻게 구축될 것인지에 대한 올바른 맥락을 알 수 있도록 많은 코드 예제를 포함시켰기 때문입니다.

먼저 이 문제를 해결해 보겠습니다. 비행은 몇 가지 추가 기능이 포함된 간단한 프레임워크를 의미합니다. Laravel, Symfony, Yii, Cake 또는 [빈칸 채우기]와 경쟁하지 않습니다. 이 프레임워크는 실제로 단순부터 중간 규모의 프로젝트를 위해 구축되었습니다. 또한 코드에서 이해하거나 훈련하기 어려운 "마법"을 좋아하지 않는 사람들에게도 적합합니다. 이는 임의의 include 문이 많은 원시 PHP 대신 이제 막 프레임워크로 분기하기 시작한 개발자를 대상으로 합니다.

tl;dr

많은 멋진 기능, 훌륭하고 간단한 구현, 어쩌구 저쩌구 여기에 코드가 있습니다. 멋진 내용을 보려면 2부로 이동하세요!

설치

Composer를 사용하여 파티를 시작해보세요.

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

새 프로젝트 구성

먼저 해야 할 일은 API 키, 데이터베이스 자격 증명 및 앱에 대한 기타 중요한 자격 증명과 같은 구성을 넣을 수 있는 app/config/config.php 파일로 이동하는 것입니다. 이 블로그에서는 SQLite 데이터베이스 경로에 대한 file_path 줄의 주석 처리를 제거하겠습니다.

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

블로그 데이터베이스 만들기

이제 비행에는 활주로라는 명령줄 유틸리티가 제공됩니다. 이를 통해 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'];

모든 것이 올바르게 설정되었는지 확인하려면 작곡가 시작을 실행한 다음 브라우저에서 http://localhost:8000/으로 이동하세요. 다음 화면이 표시됩니다.

Default Home Page

또한 모서리에는 애플리케이션에서 진행 중인 작업을 이해하는 데 도움이 되는 일부 사용자 정의 비행 패널이 포함된 편리한 디버그 도구 모음이 있음을 알 수 있습니다. 툴바의 다양한 항목 위로 마우스를 가져가면 클릭하여 페이지에 고정할 수 있는 다양한 마우스 오버가 표시됩니다(자세한 내용은 나중에 설명).

Flight Tracy Extensions

HTML 템플릿 구축

Flight에는 이미 프레임워크에 매우 기본적인 HTML 템플릿 솔루션이 포함되어 있습니다. 이는 매우 간단한 사이트나 단순한 HTML 조각을 반환하는 데 적합합니다. Latte, Twig 또는 Blade와 같은 다른 템플릿 플랫폼을 사용하는 것이 좋습니다. 이 튜토리얼에서는 훌륭하고 종속성이 없기 때문에 Latte를 사용하겠습니다(Flight에서 우리는 불필요한 종속성을 좋아하지 않는다는 것을 알 수 있습니다)!

라떼를 설치해 보세요

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>

활성 레코드 데이터베이스 클래스

Flight에는 Flight Active Record라는 데이터베이스와 상호작용하기 위한 플러그인이 있습니다. 이 플러그인은 앱에서 원시 SQL을 많이 작성하지 않도록 도와줍니다(때때로 활성 레코드/ORM/매퍼를 강제로 실행하는 대신 원시 SQL 쿼리를 작성하는 것이 더 효율적이기는 하지만). 기본적으로 활성 레코드 확장은 데이터베이스의 테이블 내의 행과 상호 작용하는 데 도움이 됩니다. 데이터베이스의 한 행을 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으로 문의하세요.