”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 使用 Flight 构建简单的博客 - 第 1 部分

使用 Flight 构建简单的博客 - 第 1 部分

发布于2024-08-01
浏览:804

Hey everyone! I figured it was time to showcase some of the new features that have been added to the Flight Framework for PHP. Earlier this year the original creator of Flight Mike Cao graciously offered to transfer ownership of mikecao/flight over to a new Flight PHP organization. Since it's been moved we've added features like middleware, route grouping, DIC, and other features. This post will be a little longer, but it's just because I've included a lot of code examples so you can have the right context into how your blog will get built.

First off, let's just get this out of the way. Flight is meant to be a simple framework with a few bells and whistles. It will not compete with Laravel or Symfony or Yii or Cake or [fill in the blank]. This framework is really built towards simple to medium size projects. It also caters to those who don't like "magic" in their code that's hard to understand or train to. It's geared more towards developers who are just starting to branch into frameworks instead of raw PHP with a lot of random include statements.

tl;dr

Lots of cool features, nice simple implementation, blah blah blah here's the code. Go to part 2 for the cool stuff!

Installation

Let's use Composer to get this party started.

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

Configure your New Project

First thing to do is to go to the app/config/config.php file where we can put any config like API keys, database credentials, and other important credentials for our app. For this blog, we'll uncomment the line with file_path for our SQLite database path:

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

Create the Blog Database

Flight now comes with a command line utility called runway. This allows you to create custom commands for a plugin for Flight, or even for your own project.

As part of the skeleton, it comes with a SampleDatabaseCommand that will give us a starting point with this blog project we are creating.

Run the below command and it should populate your database for you!

php runway init:sample-db

Next we'll open up the app/config/services.php file and uncomment the line for SQLite.

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

Just to make sure we've got everything setup correctly, run composer start and then go to http://localhost:8000/ in your browser. You should see the following screen:

Default Home Page

You'll also notice in the corner you have a handy debug toolbar with some custom Flight panels to help you understand what's going on in your application. If you hover over the various items in the toolbar, you'll see a variety of hovers that you can click on to keep sticky on the page (more on that later).

Flight Tracy Extensions

Building the HTML Templates

Flight does come with a very basic HTML templating solution already in the framework. This is just fine for very simple sites or just to return a simple piece of HTML. It is recommended to use another templating platform such as Latte, Twig, or Blade. In this tutorial, we're going to use Latte because it is awesome and has no dependencies (you'll notice in Flight we do not like unnecessary dependencies)!

Go ahead and install Latte

composer require latte/latte

Add this to your 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);
});

Now that we have a templating engine in place, we can create a base HTML file. Let's create a layout.latte file:


    
        
        {$page_title ? $page_title.' - '}Blog Built with Flight!
        {block content}{/block}
    

Active Record Database Class

Flight has a plugin for interacting with a database called Flight Active Record. This plugin helps you not write as much raw SQL in your apps (although sometimes it is more efficient to write a raw SQL query instead of forcing an active record/ORM/mapper to run it for you). Basically the active record extension helps you interact with rows within tables in your database: one row in a database can be mapped to an object in PHP (with autocomplete for the columns) saving time and sanity. Let's get it installed in our project.

composer require flightphp/active-record

Now you can use runway to create your active record classes automatically for you and it will create your properties as comments automatically (for autocomplete)!

First let's create the posts class. The first time you run this, it needs to setup the connection for the database.

$ 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

Now we'll create the comments record class:

$ 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}

My Home Page

View My Blog!

{/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:

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}

My Amazing Blog

Welcome to my blog!

Create a new post

{foreach $posts as $post} {first}

Recent Posts

{/first}

{$post->title}

By: {$post->username} on {$post->created_at|date:'d.m.Y G:i a'}

Comments: {count($post->comments)}

{$post->content|truncate:100}


Update - Delete {/foreach} {/block}

app/views/posts/show.latte

{extends '../layout.latte'}

{block content}
< Back to blog

{$post->title}

Created by: {$post->username} on {$post->created_at|date:'d.m.Y G:i a'}.

{$post->content|breakLines}

Last update: {$post->update_at|date:'d.m.Y G:i a'}.

Comments

{foreach $post->comments as $comment}

{$comment->username} on {$comment->created_at|date:'d.m.Y G:i a'}.

{$comment->content|breakLines}

Delete
{else}

No comments yet.

{/foreach}

Add comment

{/block}

app/views/posts/create.latte

{extends '../layout.latte'}

{block content}

Create a Post

{/block}

app/views/posts/edit.latte

{extends '../layout.latte'}

{block content}

Update a Post

{/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:

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!

版本声明 本文转载于:https://dev.to/n0nag0n/building-a-simple-blog-with-flight-part-1-4ap8?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何修复 PHP 通过 SSH 连接 MySQL 时的 mysqli_connect() 参数问题?
    如何修复 PHP 通过 SSH 连接 MySQL 时的 mysqli_connect() 参数问题?
    在 PHP 中通过 SSH 连接到 MySQL 服务器使用 PHP 函数通过 SSH 建立与远程 Linux 计算机上托管的 MySQL 数据库的连接可能具有挑战性。使用提供的代码时,可能会出现错误“mysqli_connect()期望参数6为字符串,给定资源”。理解问题代码尝试使用mysqli_c...
    编程 发布于2024-11-06
  • 微服务项目
    微服务项目
    ⚙️微服务项目的灵感来自@sqshq“Alexander Lukyanchikov”的piggymetrics,但是这个实现使用了PostgreSQL和更简单的业务逻辑,这个项目的主要目标是展示微服务架构的示例。 TechStack:PostgreSQL、Spring、Docker 我正在考虑可以添...
    编程 发布于2024-11-06
  • 优化 AWS ECS 的 Java 堆设置
    优化 AWS ECS 的 Java 堆设置
    我们在 AWS Elastic Container Service(ECS) Fargate 上运行多个 Java 服务 (Corretto JDK21)。每个服务都有自己的容器,我们希望使用为每个进程支付的所有可能的资源。但这些步骤可以应用于 EC2 和其他云。 服务正在运行批处理作业,延迟并不重...
    编程 发布于2024-11-06
  • PHP 初学者必备知识:释放网站的全部潜力
    PHP 初学者必备知识:释放网站的全部潜力
    PHP基础知识:释放网站潜能简介PHP是一种强大的服务器端脚本语言,广泛用于创建动态网站。对于初学者来说,掌握PHP基础知识至关重要。本文将提供一个全面的指南,涵盖PHP编程的基本要素,并通过实战案例巩固理解。安装并配置PHP要开始使用PHP,您需要安装PHP解释器和相关的软件。遵循以下步骤:- 下...
    编程 发布于2024-11-06
  • 如何确定 PHP 标头的正确图像内容类型?
    如何确定 PHP 标头的正确图像内容类型?
    确定 PHP 标头的图像内容类型使用 Header() 函数从 Web 根目录之外显示图像时,用户可能会遇到困惑关于指定的内容类型:image/png。然而,尽管内容类型固定,但具有各种扩展名的图像(例如, JPG、GIF)仍然可以成功显示。要解决这种差异,动态确定正确的扩展名至关重要基于文件扩展名...
    编程 发布于2024-11-05
  • ByteBuddies:使用 Python 和 Tkinter 创建交互式动画宠物
    ByteBuddies:使用 Python 和 Tkinter 创建交互式动画宠物
    大家好! 我很高兴向大家介绍 ByteBuddies,这是一个用 Python 和 Tkinter 创建的个人项目,展示了交互式动画虚拟宠物。 ByteBuddies 将引人入胜的动画与用户交互相结合,提供了展示 GUI 编程强大功能的独特体验。该项目旨在通过提供交互式虚拟宠物来让您的屏幕充满活力...
    编程 发布于2024-11-05
  • 如何解决“TypeError:\'str\'对象不支持项目分配”错误?
    如何解决“TypeError:\'str\'对象不支持项目分配”错误?
    'str'对象项分配错误疑难解答尝试在 Python 中修改字符串中的特定字符时,您可能会遇到错误“类型错误:“str”对象不支持项目分配。”发生这种情况是因为 Python 中的字符串是不可变的,这意味着它们无法就地更改。解决此问题的一种常见方法是将字符串转换为可变列表,进行必要的...
    编程 发布于2024-11-05
  • 如何缓解 GenAI 代码和 LLM 集成中的安全问题
    如何缓解 GenAI 代码和 LLM 集成中的安全问题
    GitHub Copilot and other AI coding tools have transformed how we write code and promise a leap in developer productivity. But they also introduce new ...
    编程 发布于2024-11-05
  • Spring 中的 ContextLoaderListener:必要的邪恶还是不必要的复杂?
    Spring 中的 ContextLoaderListener:必要的邪恶还是不必要的复杂?
    ContextLoaderListener:必要的邪恶还是不必要的复杂?开发人员经常遇到在 Spring Web 应用程序中使用 ContextLoaderListener 和 DispatcherServlet。然而,一个令人烦恼的问题出现了:为什么不简单地使用 DispatcherServlet...
    编程 发布于2024-11-05
  • JavaScript 机器学习入门:TensorFlow.js 初学者指南
    JavaScript 机器学习入门:TensorFlow.js 初学者指南
    机器学习 (ML) 迅速改变了软件开发世界。直到最近,得益于 TensorFlow 和 PyTorch 等库,Python 仍是 ML 领域的主导语言。但随着 TensorFlow.js 的兴起,JavaScript 开发人员现在可以深入令人兴奋的机器学习世界,使用熟悉的语法直接在浏览器或 Node...
    编程 发布于2024-11-05
  • extjs API 查询参数示例
    extjs API 查询参数示例
    API 查询 参数是附加到 API 请求 URL 的键值对,用于向服务器发送附加信息。它们允许客户端(例如 Web 浏览器或应用程序)在向服务器发出请求时指定某些条件或传递数据。 查询参数添加到 URL 末尾问号 (?) 后。每个参数都是一个键值对,键和值之间用等号 (=) 分隔。如果有多个查询参数...
    编程 发布于2024-11-05
  • 如何解决Go中从不同包导入Proto文件时出现“Missing Method Protoreflect”错误?
    如何解决Go中从不同包导入Proto文件时出现“Missing Method Protoreflect”错误?
    如何从不同的包导入 Proto 文件而不遇到“Missing Method Protoreflect”错误在 Go 中,protobuf 常用于数据序列化。将 protobuf 组织到不同的包中时,可能会遇到与缺少 ProtoReflect 方法相关的错误。当尝试将数据解组到单独包中定义的自定义 p...
    编程 发布于2024-11-05
  • 为什么MySQL在查询“Field = 0”非数字数据时返回所有行?
    为什么MySQL在查询“Field = 0”非数字数据时返回所有行?
    不明确的查询:理解为什么 MySQL 返回“Field=0”的所有行在 MySQL 查询领域,一个看似无害的比较,例如“SELECT * FROM table WHERE email=0”,可能会产生意外的结果。它没有按预期过滤特定行,而是返回表中的所有记录,从而引发了对数据安全性和查询完整性的担忧...
    编程 发布于2024-11-05
  • 服务器发送事件 (SSE) 的工作原理
    服务器发送事件 (SSE) 的工作原理
    SSE(服务器发送事件)在 Web 开发领域并未广泛使用,本文将深入探讨 SSE 是什么、它是如何工作的以及它如何受益您的申请。 什么是上交所? SSE 是一种通过 HTTP 连接从服务器向客户端发送实时更新的简单而有效的方法。它是 HTML5 规范的一部分,并受到所有现代 Web ...
    编程 发布于2024-11-05
  • 如何从字符串 TraceID 创建 OpenTelemetry Span?
    如何从字符串 TraceID 创建 OpenTelemetry Span?
    从字符串 TraceID 构造 OpenTelemetry Span要建立 Span 之间的父子关系,必须在上下文传播不可行的情况下使用标头。在这种情况下,跟踪 ID 和跨度 ID 包含在消息代理的标头中,这允许订阅者使用父跟踪 ID 创建新的跨度。解决方案以下步骤可以使用跟踪 ID 在订阅者端构建...
    编程 发布于2024-11-05

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3