六、搭建 PHP 框架

在上一章中,我们创建了一个模型和一个控制器,其中控制器Contacts类实例化了模型Contact类。我们成功地使用了一个namespace、一个use语句、一个方法、一个访问修饰符、一个对象和一个类。我们在前一章中已经看到了框架的力量。

在本章中,我们将从头开始构建 MVC 框架。框架实际上只是组织代码和构造代码的一种方式。从一个空目录开始,我们将构建一个完整的工作框架,作为更复杂应用的起点。

在上一章中,我们从数组中检索数据。在本课中,我们将从数据库中检索它。

在本章结束时,您将能够:

  • 构建一个基本的 PHP MVC 框架
  • 实现前面章节中介绍的 OOP 概念
  • 确定如何将控制器路由到指定的 URI
  • 使用 PHP 数据对象(PDO)与数据库交互
  • 使用 HTML 构建和创建可重用页面(视图)

我们还将实现前面章节中介绍的 OOP 概念,包括但不限于名称空间、use语句、对象和类、访问修饰符和方法。

我们将学习如何将控制器路由到指定的 URL,并使用 HTML 构建和创建可重用的页面(视图)。最后,我们将使用 PDO 与数据库交互。

建立项目开发环境

本节涉及建立项目开发环境。

这是关于设置索引、.htaccess文件、创建 web 根目录、设置编写器以及设置app目录的所有内容。

  • 索引是框架的引导文件;这最终是接收所有请求的地方。例如,当用户在地址栏中输入 URL 时,就会发出请求。
  • .htaccess是将所有请求传递到索引文件的 mod rewrite 引擎。
  • Web root是浏览器可以访问的公用文件夹,用于存储所有 Web 应用资产的索引和.htaccess。这将包括图像、CSS 和 JavaScript 文件。
  • Composer是一个包管理器,用于管理系统所依赖的代码库。
  • 应用目录是您的应用;它将存储视图、模型、控制器和辅助对象。Helpers 是一种紧凑的方法,用于帮助开发人员经常遇到的单个常见任务。开发人员经常会发现自己在重复相同的任务,并且会使用单个或多个方法创建一个帮助器类来帮助完成此任务。这可能是格式化日期、执行特定计算等。

引导标志着建立框架的过程通常被称为引导。这不能与流行的名为 Bootstrap 的 CSS 网格混淆。这基本上将框架的所有核心部分紧密地联系在一起。

使用 Composer 和 Whoops 报告错误

对于这个项目,我们将使用 Whoops 库来处理错误。Whoops 库是检查项目中可能出现的错误的工具。此库已打包,可供其他开发人员在其项目中使用。

当 PHP 中发生错误时,您可以使用“哇”来查看此显示信息,而不是服务器上的标准平淡错误报告:

Error Reporting Using Composer and Whoops

Composer 将管理此依赖项的使用,因为 PHP 开发人员认为它是一个使用非常广泛且非常流行的包管理器。

Composer 是 PHP 中的依赖项管理工具。它允许您声明项目所依赖的库,并为您管理(安装/更新)它们。要安装 Composer,请转至https://getcomposer.org/download/

设想一个场景,您必须为 PHP 安装一个依赖项,要安装该依赖项,您需要安装其他开销依赖项。Composer 可帮助您处理此问题。它用于处理安装库的所有工作,因为它将所有库和依赖项一起下载。

设置作曲家

我们将在本节中介绍如何设置 Composer。要执行此操作,请执行以下步骤:

  1. Create a folder to store the framework files.

    你可以随意调用你的文件夹,只要它是小写的,没有空格。

  2. Next, we will set up Composer. Create a file in the root of your framework folder called composer.json.

    该文件包含一个 JSON 对象,该对象将根据需要自动加载类。我们将使用 PSR-4 自动加载。

    PSR-4 自动加载将在使用类时根据其名称空间加载该类。例如,新的App\Models\Contact()将告诉 Composer 自动加载一个名为Contact的文件,该文件存储在文件夹app\Models.

  3. 打开composer.json并创建AppSystem定义。

  4. 这将告诉 Composer,我们所调用的所有内容都是一个名称空间,使用AppSystemappsystem文件夹中查找该类。
  5. 我们还加载了一个名为Whoops.的第三方包。我们通过将此包作为依赖项包含在require

    php { "autoload": { "psr-4": { "App\\" : "app/", "System\\" : "system/" } }, "require": { "filp/whoops": "^2.1" } }

    中来加载此包 7. 现在保存composer.json.,在webroot,中创建两个文件:index.php.htaccess. 8. 打开.htaccess。 9. 出于安全原因,如果文件夹不包含index文件,我们不希望其内容显示在浏览器中。要禁用目录浏览,请输入:

    php Options –Indexes

  6. Next, a check is made to ensure mod rewrite is enabled:

    php <IfModule mod_rewrite.c> //more code </IfModule>

    modrewrite 提供了一个基于规则的重写引擎,可以动态重写请求的 URL。它有助于创建 URL,因此index.php?page可以成为/页面。

  7. 接下来,打开重写引擎并将底部设置为该文件夹的根:

    php RewriteEngine On RewriteBase /

  8. 要强制使用 HTTPS,您可以取消下面的#注释,但只能在启用 HTTP 的服务器上执行此操作。

  9. Next, define the rewrite conditions.

    这将忽略尾部斜杠以及存在的文件夹和文件。只应路由动态文件,例如,不作为物理文件存在的 URL。

    最后一条规则将所有请求传递给index.php?$1.,而$1是请求 URL 中第一个/之后的请求。

    RewriteCond基本意思是“只有在这是真的情况下才执行下一个RewriteRule

    RewriteRule基本上意味着如果完成的请求与^(.+)$匹配(匹配除服务器根目录外的任何 URL),则将被重写为index.php?$1,,这意味着联系人请求将被重写为index.php?contact:

    重写规则^(.*)$index.php?$1[QSA,L]

    QSA 意味着该标志强制重写引擎将替换字符串中的查询字符串部分附加到现有字符串中,而不是替换它。

    安全套接字层(SSL)在 web 服务器和 web 浏览器之间创建加密连接。这将停止从您的计算机截取到 web 服务器的任何数据。建议使用 HTTPS。

    完整的文件应该如下所示:

    ```php

    Disable directory snooping

    Options -Indexes

    # Uncomment the rule below to force HTTPS (SSL)
    

    ……….. RewriteRule ^(.*)$ index.php?$1 [QSA,L] ```

    有关完整的代码片段,请参阅 code files 文件夹中的 Lesson 6.php 文件。

  10. 保存文件。现在,打开index.php.

  11. First, start php and then do a check to determine if vendor/autoload.php exists (it won't exist yet) and require the file.

    这是重要的一步。autoload.php 文件仅在编写器初始化后才存在。在需要文件之前进行检查是一种预防措施,用于避免致命错误。

  12. 我们应该通知用户作曲家的要求以及去哪里获取。我们使用一个else子句:

    php if(file_exists('../vendor/autoload.php')){ require '../vendor/autoload.php'; } else { echo "<h1>Please install via composer.json</h1>"; echo "<p>Install Composer instructions: <a href='https://getcomposer.org/doc/00-intro.md#globally'>https://getcomposer.org/doc/00-intro.md#globally</a></p>"; echo "<p>Once composer is installed navigate to the working directory in your terminal/command prompt and enter 'composer install'</p>"; exit; }

    来实现这一点 17. 接下来,我们将设置我们的环境。 18. We will define a constant called ENVIRONMENT and give it a value of development. When going into production, set the environment to production.

    在生产环境中,您不希望显示错误。使用环境常量是设置应用环境的好方法:

    php define('ENVIRONMENT', 'development');

  13. Now, based on the environment constant, we can set the appropriate level of error reporting:

    php if (defined('ENVIRONMENT')){ switch (ENVIRONMENT){ case 'development': error_reporting(E_ALL); break; case 'production': error_reporting(0); break; default: exit('The application environment is not set correctly.'); } }

    在开发模式下,将显示所有错误,但在生产模式下,不会显示任何错误。

    完整的文件如所示:

    ```php <?php if(file_exists('../vendor/autoload.php')){ require '../vendor/autoload.php'; } else { …… error_reporting(0); break; default: exit('The application environment is not set correctly.'); }

    } ```

    有关完整的代码片段,请参阅 code files 文件夹中的 Lesson 6.php 文件。

    现在将创建一个名为 vendor 的新文件夹。此文件夹是 Composer 安装所需文件和任何第三方依赖项的位置。

  14. You can now go back to your browser and reload the page. You should now see a blank page.

    这意味着 Composer 正在工作,但我们尚未请求加载任何内容。

    打开 Whoops 包时,视图中的错误将在屏幕上显示错误,并显示框架如何执行代码的完整堆栈跟踪。这可以帮助开发人员通过遵循他们的代码所经过的路径来隔离问题。

活动:使用 Composer 安装依赖项

假设您正在处理一个 PHP 项目,并且您的项目需要很多依赖项。您正处于一个严格的截止日期,但是,在添加这些依赖项之前,您无法继续进行。您发现可以使用 Composer 自动安装依赖项。您现在需要安装 Composer。

本活动的目的是让您熟悉 Composer 安装。

要执行此活动,请执行以下步骤:

  1. 通过打开终端或命令提示符运行框架。
  2. If on Windows, navigate to the framework folder and launch the php server:

    php php –S localhost:8000 –t Webroot

    –S表示运行服务器,使用 localhost:8000 作为地址,–t Webroot将文档root设置到Webroot文件夹中。

    终端输出的外观如下所示(某些细节在您的系统上会有所不同):

    php PHP 7.1.4 Development Server started at Wed Nov 29 20:37:27 2017 Listening on http://localhost:8000 Document root is /Users/davidcarr/Dropbox /projects/localsites/framework/webroot Press Ctrl-C to quit.

  3. 现在,转到http://localhost:8000,您将看到我们在index.php.中的else语句中所写的作曲家说明

  4. This is because we have not yet set up Composer. We do this by typing the following in the Terminal:

    ```php composer install

    ```

    输出如下:

    php Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 2 installs, 0 updates, 0 removals - Installing psr/log (1.0.2) Loading from cache - Installing filp/whoops (2.1.14) Downloading: 100% filp/whoops suggests installing symfony/var-dumper (Pretty print complex values better with var-dumper available) filp/whoops suggests installing whoops/soap (Formats errors as SOAP responses) Writing lock file Generating autoload files

  5. 请注意,现在将创建一个名为vendor的新文件夹。此文件夹是 Composer 安装所需文件和任何第三方依赖项的位置。

  6. Now, go back to your browser and reload the page.

    您现在应该看到一个空白页。

    这意味着 Composer 正在工作,但我们尚未请求加载任何内容。

  7. Go back to index.php in your editor and add these lines at the bottom of the file:

    ```php //initiate config $config = App\Config::get();

    new System\Route($config); ```

    这将加载我们的config类并设置我们的路由。

  8. Save index.php and create a new file called Config.php inside the app folder.

    注意将文件命名为Confignot config.区分大小写对于基于 Unix 的系统(如 Mac 和 Linux)很重要。

我们已经到了本节的末尾。我们已经学习了如何引导一个允许单个入口点的应用,以及如何使用 Composer 自动加载类。我们介绍了如何处理错误,最后介绍了框架的构建过程。

在下一节中,我们将设置配置类,还将设置路由。

配置类、默认类、路由

在本节中,我们将了解configuration类,并且我们还将设置路由。

我们将开设config课程。这将位于app文件夹的根目录中。config类存储默认控制器、要加载的default方法和数据库凭据。在开头的index文件中,您将把config类传递给route类。route类控制加载的内容和时间。现在的焦点是configuration类和路由。其他组件将在后面的章节中详细介绍。

configuration类是框架的一系列选项,包括以下内容:

  • 数据库源凭据
  • 默认控制器的路径
  • 默认方法的路径

在本节中,我们还将创建一个视图类,该类负责加载视图,从而可以显示表示层的位置。

在设置路由时,我们会通知框架在与 URL 匹配的文件系统中查找的位置。

加载正确的文件时,这将是所需的控制器类。我们将激活所需的方法、所需的模型和所需的视图。

我们将做所有这些,这样用户就可以在浏览器中看到他们通过简单单击链接(即向服务器发出请求)所请求的内容。

然后,我们将创建类route类,该类从 URL 获取段,以便它知道要加载哪个控制器和方法以及要传递的参数。

例如,URLhttp://localhost:8000/contacts/view/2表示转到联系人控制器查看方法。在本例中,数字 2 表示传递给 view 方法的参数。

configuration类通常被开发人员称为 config 类。

配置是一个自然的地方,用户可以在这里寻求帮助,以了解如何记住有关其框架项目的重要细节。建议开发人员开发一个系统来记住他们项目的细节。

如果他们计划使项目开源,这会很有帮助。如果开发人员需要在以后记住有关项目的详细信息,那么它也会对开发人员有所帮助,因为在开发人员需要重新访问项目之前,可能需要几个月甚至几年的时间。

这些可能是什么样的细节?

  • 版本号-随着时间的推移,开发人员可能会进行添加和改进,这可能会影响代码库的核心。了解您正在使用的版本可以帮助您在以后选择正确的编程方法。
  • 学分-对您使用过其作品的其他开发人员的作品进行学分是一种良好的做法。如果您未能做到这一点,您可能会收到来自未经认证开发人员的不愉快电子邮件。
  • Author details – Users of open source projects may benefit from contact details of the original developer. Annoyed uncredited developers need somewhere to send that unhappy email.

    Configuration Class, Default Classes, and Routing

    下面是一个 Config 类的示例

加载视图文件

我们将通过一个示例演示在本节完成后加载视图文件的能力。但是,在这个阶段还没有创建视图,所以使用了一个自定义 404 页面来代替它。

本节中的示例在浏览器中加载框架。最初,由于找不到视图,您将在浏览器中看到 404 消息。这是因为默认控制器不存在。

views文件夹中存在一个示例404 php文件,其消息为“无法找到该文件”。保存文件并刷新新创建的 404 页面的浏览器。

  1. Open php and give the file a namespace of App.

    该类属于应用命名空间,因为它存储在应用文件夹中。

  2. Next, define a class called Config and create a method called get.

    get方法需要返回一个数组。阵列的键将是用于路由和数据库凭据的设置:

    有关完整的代码片段,请参阅代码文件文件夹中的Lesson 6.php文件。

    ```php <?php namespace App;

    class Config { …… public static function get() 'db_name' => 'mini', 'db_username' => 'root', 'db_password' => '', ]; } } ```

    前面的名称空间定义包含路径App\Controllers。注意双反斜杠这是因为反斜杠经常被转义,所以使用双反斜杠可以阻止它被转义。

    当我们编写路由器时,名称空间定义、默认控制器和默认方法将变得清晰。

  3. 最后,设置数据库属性。

  4. 设置要使用的数据库类型及其位置的数据库属性,然后是数据库名称以及访问数据库的用户名和密码。
  5. You will need to access a MySQL database in order to create a database. To set up a native database, MariaDB is recommended. To download MariaDB, follow the instructions at https://mariadb.com/downloads/mariadb-tx.

    在本例中,我们有一个名为 mini 的数据库,我的用户名是 root。我们没有密码,所以将其留空。

  6. 保存Config.php文件。

  7. 在建立routing类之前,我们需要创建一个View类。此类将负责加载view文件,并在找不到 URL 时显示 404 页面。
  8. 在系统中,创建一个名为View.php.的新文件
  9. Open php and set the namespace to System. Next, define a class called View and create a method called render that accepts two parameters, $path and $data.

    $path将保留请求文件的路径。

    $data将保存要传递到view文件的内容。

    $data是可选的;注:其默认值为false。这意味着,如果只有一个参数传递给render方法,则不会使用该数据。

    在方法 ID 内部,一个布尔值检查$data.是否为false,,它被忽略;否则,使用foreach循环来循环数据。在每个循环中,数据被提取到一个局部变量。

  10. 循环完成后,将视图文件存储的相对路径设置为app/views/后接请求的视图。

  11. Finally, a check is made to ensure the view file exists and requires it, otherwise an error is generated:

    有关完整的代码片段,请参阅代码文件文件夹中的Lesson 6.php文件。

    ```php <?php namespace System;

    / * View - load template pages * / class View { ……. } else { die("View: $path not found!"); }

    }
    

    } ```

  12. 保存文件并在system文件夹中创建一个名为Route.php的新文件。

  13. 打开 php 并将名称空间设置为System.
  14. The View class we've just created needs to be available to this class. To import it, add:

    php use System\View;

    这将加载View文件。PHP 之所以知道在哪里可以找到文件,是因为名称空间是编写器的作用所在。能够以这种方式导入类真的很有帮助。

  15. 现在,创建一个名为Route的类和一个名为__construct,的方法,该方法需要一个名为$config:

    ```php <?php namespace System;

    use System\View; class Route { public function __construct($config) { ```

    的参数 16. Now, set up the following variables:

    php $url = explode('/', trim($_SERVER['REQUEST_URI'], '/')); $controller = !empty($url[0]) ? $url[0] : $config['default_controller']; $method = !empty($url[1]) ? $url[1] : $config['default_method']; $args = !empty($url[2]) ? array_slice($url, 2) : array(); $class = $config['namespace'].$controller;0

    $url将以/page/request 的形式保存来自请求路由的数组。这就是它的工作原理:当运行 explode 时,它会在请求的 URI 中找到一个正斜杠,$\u 服务器会提供该 URI。

    接下来,$controller方法使用三元运算符检查$url 的 0 索引是否存在,否则使用 Config 类中定义的默认 _ 控制器。

    $方法检查$url[1]是否存在,否则从 config 类读取。

    $args 将在前 2 个之后获取$url 的所有其他索引。

    $class保存Config类中设置的控制器路径。

    这些参数所做的是从请求的 URL 获取控制器、方法和参数。例如:

    http://localhost:8000/contacts/view/2

    这导致:

    联系人=联系人类别。

    View=contacts 类中的 View 方法。

    2=传递给方法的参数。

    如果请求的 URL 为 http://localhost:8000/,则不请求任何控制器或方法,因此将使用默认的控制器和方法,如system\Config.php.中所述

  16. 这些变量设置完成后,进行检查,即如果该类不存在,则调用Route类(尚未设置)中存在的not_found方法:

    php //check the class exists if (! class_exists($class)) { return $this->not_found(); }

  17. 接下来,检查该方法以确保其存在:

    php //check the method exists if (! method_exists($class, $method)) { return $this->not_found(); }

  18. 接下来,设置类的实例:

    php //create an instance of the controller $classInstance = new $class;

  19. 通过调用call_user_func_array运行该类,并传入该类实例和方法的数组,并将任何参数作为第二个参数传递:

    php //call the controller and its method and pass in any arguments call_user_func_array(array($classInstance, $method), $args);

  20. If a route is called that does not exist, a not_found method is needed. This calls the render method and passes 404 as the parameter. This will attempt to load app/view/404.php, should it exist:

    php //class or method not found return a 404 view public function not_found() { $view = new View(); return $view->render('404'); }

    整个类如下所示:

    有关完整的代码片段,请参阅代码文件文件夹中的Lesson 6.php文件。

    ```php <?php namespace System;

    use System\View;

    class Route ……. { $view = new View(); return $view->render('404'); } } } ```

操纵输出

本节将向您展示如何操作上一个示例的输出。以下是执行此操作的步骤:

  1. Load up the framework http://localhost:8000 and you will see the following output:

    php View: 404 not found!

    这是因为默认控制器不存在,app/views/404.php 也不存在。

  2. app文件夹中创建一个views文件夹,并创建一个名为404.php的文件。输入诸如“The file cannot be found.”之类的消息并保存文件。

  3. 在浏览器中重新加载框架,您现在将看到您的消息。

在本节中,我们介绍了configuration类,在该类中,我们看到配置类如何位于root文件夹的顶部。我们还了解了如何设置路由,在这里我们执行了一个view页面的加载。

在下一节中,我们将介绍基本控制器,它定义了 MVC 框架的主要功能。

基本控制器、默认状态和路由

由于 MVC 框架的性质,基本控制器类需要默认状态。

默认视图由默认控制器类中的默认方法加载。

从该默认控制器类中,将加载系统中的所有其他控制器。

默认Controller类和默认方法的创建将是我们在本节中构建的重点。

不必将模型包含为控制器,视图可以在没有数据源的情况下独立工作。

设置基本控制器、默认状态和路由

在本节中,我们将介绍如何设置基本控制器、默认状态和路由。以下是步骤:

视图:

  1. Now, let's set up the default view. Create a file called default.php inside app\views and write the content of `Hello from default view.` or any other message.

    这将在框架的主页上显示。

控制器:

在开始构建应用控制器之前,我们需要一个所有其他控制器都可以扩展的基础控制器。这样做的原因是控制器可以使用基本控制器中定义的任何属性或方法。

  1. 创建一个名为BaseController.php的新文件,并将其保存在system文件夹中。
  2. 打开 php 并将名称空间设置为System。定义一个名为BaseController的类。
  3. 定义两个名为$view$url的类属性。这两个属性都有一个访问修饰符 public,这意味着只要使用了BaseController,这些属性都是可用的。
  4. 接下来,创建一个construct方法,然后设置View类的一个新实例。这样就可以使用$this->viewextended控制器中调用viewrender方法。
  5. 接下来,为属性$this->url分配一个方法getUrl()。这将调用另一个方法来获取当前 URL。
  6. Now, a check is run on the environment mode. If it's set to development, then a new instance of the Whoops error handler is created. This Whoops class is brought in by Composer, as defined by the composer.json file.

    当代码在浏览器中运行时出现错误时,Whoops类将提供丰富的错误堆栈跟踪。

  7. Finally, a getUrl() method is defined that will return the requested URL:

    有关完整的代码片段,请参阅代码文件文件夹中的Lesson 6.php文件。

    ```php <?php namespace System;

    use System\View;

    class BaseController {

    public $view; ……… $url = isset($_SERVER['REQUEST_URI']) ? rtrim($_SERVER['REQUEST_URI'], '/') : NULL; $url = filter_var($url, FILTER_SANITIZE_URL); return $this->url = $url; }

    } ```

家庭控制器:

  1. app/Config.php,中,我们将default_controller设置为家:

    ```php //set default controller 'default_controller' => 'Home',

    //set default method 'default_method' => 'index', ```

  2. Let's create this now. Create a Controllers folder inside the app folder and create a file called Home.php.

    所有类别都应该以大写字母开头,后面的每个单词都应该大写。

  3. 打开 php 并将名称空间设置为App\Controllers。此命名空间引用文件夹结构。

  4. 接下来,通过调用BaseController的名称空间和调用名称来导入它。
  5. Define a class called Home and extend the BaseController.

    这将允许控制器访问$this->view并加载视图。

  6. 创建一个名为index的方法,然后返回$this->view->render并传递要加载的文件名。

  7. 在这种情况下,默认传入,加载app\views\default.php

    ```php <?php namespace App\Controllers;

    use System\BaseController;

    class Home extends BaseController { public function index() { return $this->view->render('default'); } } ```

活动:探索结果

我们现在可以看到任务的输出,如演示文件所示。请按照以下步骤执行此操作:

  1. 在浏览器http://localhost:8000中打开您的框架,您将看到您的默认视图文件正在加载。
  2. 还记得那节叫喊课吗?好吧,让我们看看它的实际效果。打开您的default.php视图文件,并在文件末尾添加此代码。打开 php 并编写一些东西,但不要用字符串。代码应如下所示:

    php Hello from default view. <?php ohno!

  3. Now, save and reload the page in the browser and you will see:

    Activity: Exploring the Results

    此页面告诉错误是什么,但也显示了问题所在的代码片段和完整的堆栈跟踪,以便您可以跟踪从执行到失败的过程:

  4. 现在,删除default.php中的修改,使其仅包含原始内容,保存并重新加载页面。您将再次看到页面正常加载。

  5. 现在,让我们看看如何访问一个新方法。在家庭控制器中,创建一个名为packt的新方法,加载名为packt的视图。
  6. 在 app\views 中创建一个名为packt.php的新视图文件,并输入文本“Hello from Pack!'
  7. 进入home/packt http://localhost:8000/home/packt加载页面。

您现在将看到您的packt视图文件的内容。

在本节中,我们更好地了解了默认状态在项目中扮演的角色。此项目需要这些基本方法来最初运行并从中扩展。

我们通过构建默认状态(包括baseControllerbaseMethod)获得了经验。

在下一节中,我们将学习 PDO,这是一个轻量级接口,用于访问 PHP 中的数据库。

与 PDO 合作

在本节中,我们将创建 PDO 包装器,并使用数据库作为模型中的数据源。

从这一节开始,我们将获得在我们的框架项目中使用数据库的能力。

这里将介绍六种方法。

我们有一个get方法——这是为了创建到数据库的连接,并确保它是一个单实例,这意味着它只能有一个实例:

  • 我们有一个raw方法来运行原始的、不安全的查询
  • 一种select方法,用于运行安全查询以从数据库中选择记录
  • 创建新记录的insert方法
  • 更新记录的update方法
  • 一种delete删除记录的方法
  • 清空表格的truncate方法

这样做的目的是让你的积垢工作。没有这个类,CRUD 功能就不可能实现。

基本模型是我们使用数据库助手创建数据库连接的地方。

这将允许其他模型类从此模型扩展并使用数据库连接。这只包括一种方法:

构造:

这负责将配置传递给数据库帮助器以创建数据库连接。

现在,我们可以开始使用数据库了。

数据库访问:如果您发现您没有访问数据库客户端或 PHP 管理 web 界面的权限,则包含一个回退选项,所有学生都可以使用该选项创建数据库并插入数据。

接下来的部分将介绍如何在 apps 文件夹中创建第一个模型。我们将创建模型contact.php,并讨论最佳实践和命名约定,以及从先前创建的基础模型进行扩展,同时设置一种显示数据库记录的方法。

接下来,我们将创建一个从基本控制器扩展而来的contact控制器。在索引方法上调用联系人模型并将记录从模型传递到视图之前,导入联系人模型。在该视图中,我们将查看记录并每行显示一条记录。

然后,我们打开浏览器,转到“联系人”控制器,查看页面上显示的联系人。

要加载不同的控制器,其过程与前面的子主题中描述的过程相同。创建控制器,设置其名称空间,并定义要存在于基本控制器中的类,或者在调用控制器名称时使用加载的索引方法,或者使用其他名称并通过调用 controllername/methodname 来访问它。

我们的框架设置快结束了。现在,我们可以创建控制器和方法来加载页面并将数据传递给视图。对于一个静态站点来说,这很好,它将使您的代码保持组织和快速运行,但缺少的一个重要组件是使用数据库的能力。

在这一点上,我们将创建一个数据库助手。这是一个存储在名为helpers的公用文件夹中的类的别致名称。Helpers 是不适合控制器或模型的类,但是扩展功能的独立类。

数据库助手将有六种方法:

  • get()-设置数据库连接
  • raw()-运行原始的、不安全的查询
  • select()-运行查询以从数据库中选择记录
  • insert()-创建新记录
  • update()-更新现有记录
  • delete()-删除现有记录
  • truncate()-清空一张桌子

创建联系人控制器并查看记录

在本节中,我们将开始创建联系人控制器。请按照以下步骤执行此操作:

  1. 首先,在app文件夹内创建一个名为Helpers的文件夹,并创建一个名为Database.php的新文件。
  2. 打开 php 并将名称空间设置为App\Helpers.
  3. Next, we need to import PDO.

    PDO 是一个数据库抽象层;它是一个包装器,支持 12 种不同的数据库引擎,包括 MySQL。这就是我们将用来与数据库对话的内容。

  4. 要导入 PDO,请使用use语句:

    ```php <?php namespace App\Helpers;

    use PDO; ```

  5. 接下来,定义一个名为Database的类,该类扩展了PDO。在类内部创建一个名为$instances的属性,并将其设置为数组数据类型。

  6. $instances属性将用于确保只有一个数据库连接在使用:

    php class Database extends PDO { protected static $instances = array();

  7. 接下来,创建一个名为get()的方法,该方法接受一个名为$config的婴儿车。这将是在app\Config.php.中设置的Config

  8. 在此方法中,设置本地变量以保存数据库凭据。这些值将从$config数组中提取。
  9. 然后,创建一个名为$id的变量。这将保存所有数据库局部变量以创建标识符。接下来,执行检查以检查$instance属性是否已经具有此$id
  10. If $instances does have the $id, then it will return the $instance, otherwise a new PDO connection is attempted.

    连接到 PDO 时,将传递数据库凭据,并将字符集设置为 UTF-8。

  11. 在下一行,错误模式设置为异常。这意味着将导致并显示任何异常。

  12. Now, set the $instance to the current connection and return $instance:

    有关完整的代码片段,请参阅代码文件文件夹中的Lesson 6.php文件。

    ```php GET method

    public static function get($config) { // Group information ……. // Setting Database into $instances to avoid duplication self::$instances[$id] = $instance;

    //return the pdo instance return $instance;

    } ```

原始方法

  1. Create a method called raw. This is a very simple method. It accepts a single parameter which is an SQL statement. The $sql is passed to $this->query, which will then run the query directly:

    这对于执行不需要安全的查询非常有用。如果没有进行检查,查询将按原样执行并返回结果。

    php public function raw($sql) { return $this->query($sql); }

选择方式:

  1. Next, create a method called select(). This will accept four parameters:

    • $sql–SQL 查询
    • $array–要绑定到查询的任何键(可选)
    • $fetchMode–将 PDO 获取模式默认设置为对象(可选)
    • $class–用于指定一个类,该类将与获取模式一起使用

    在方法内部,因此您不必编写$this->db->select('SELECT * FROM table') w,我们将向 SQL 查询添加 select,前提是它还不存在。这是通过将大小写更改为小写,然后使用substr检查$sql.的前七个字母。如果不等于 select,则将 select 添加到开头。

  2. 接下来,准备查询。这将设置 SQL 查询,而不运行它。接下来,循环$array,并将任何值分配给特定的数据类型。如果值是 INT,则使用 PARAM_INT 的数据类型,否则该数据类型将使用 string。

  3. Finally, the execution is run. This passes the $sql to the server and the binded $array keys separately, meaning there is no way a SQL injection can ever happen, resulting in a secure query.

    执行查询后,将重新调整响应。默认情况下,将返回一个对象:

    有关完整的代码片段,请参阅代码文件文件夹中的Lesson 6.php文件。

    php public function select($sql, $array = array(), $fetchMode = PDO::FETCH_OBJ, $class = '') { …… return $stmt->fetchAll($fetchMode); } }

插入方式

  1. 要将新记录插入数据库,需要进行 insert 查询。使用两个参数创建名为 insert 的新方法:
    • $table–表名
    • $data–要插入到$table 中的键和值的数组
  2. 使用 ksort($data)对$data 数组进行排序。
  3. 接下来,将所有数组键提取到名为$fieldNames的变量。这是通过使用内爆并在每个键之间设置逗号并针对$data 运行array_keys()来完成的。
  4. 现在,再次执行同样的操作,这次添加,``:作为内爆选项,并将其保存到名为$fieldValues 的变量中。
  5. 然后,使用$this->prepare,可以编写一个 SQL 命令,将$fieldNames设置为$table$fieldValues值。在$data上循环并绑定值。
  6. 最后,返回最后一条插入记录的 ID。当您在插入记录后立即需要主键时,此选项非常有用:

    ```php public function insert($table, $data) { ksort($data); $fieldNames = implode(',', array_keys($data)); $fieldValues = ':'.implode(', :', array_keys($data));

    $stmt = $this->prepare("INSERT INTO $table ($fieldNames) VALUES ($fieldValues)");

    foreach ($data as $key => $value) { $stmt->bindValue(":$key", $value); } $stmt->execute(); return $this->lastInsertId(); } ```

更新方式

  1. 接下来,用三个参数创建一个名为 update 的方法:
    • $table——表名
    • $data-要更新的数据数组
    • $where–用于放置条件的键和值的数组,例如,['id'=>2],其中 id 等于 2
  2. $data进行排序,然后提取$data
  3. 在循环内部,附加到名为$fieldData的变量。添加$key = :$key。接下来,通过调用trim()删除右侧的所有空白。
  4. 接下来,循环遍历$where数组。在每个循环上,分配$key = :$key。这将创建一个占位符列表,供绑定稍后捕获。
  5. 再次,将所有空格向右修剪,然后使用$this->prepare并在表名中写入更新 SQL 过程,后跟fieldDetailsWhereDetails
  6. 接下来,将键绑定到:$key占位符并执行查询。
  7. The last step is to return a rowCount(). This is the number of records that have been updated:

    有关完整的代码片段,请参阅代码文件文件夹中的Lesson 6.php文件。

    ```php public function update($table, $data, $where)

    {

    ksort($data);

    $fieldDetails = null; ……. } $stmt->execute(); return $stmt->rowCount(); } ```

删除方式

  1. 创建一个名为 delete 的新方法,该方法有三个参数:
    • $table-表的名称
    • $where–确定 where 条件的键值数组
    • $limit–要删除的记录数,默认值为 1,传递 null 以删除数量限制
  2. 在方法内部,通过$where$where循环进行排序,并根据数组的键和值设置占位符。
  3. 准备查询并在$table$where$limit中写入传递的 SQL 命令。
  4. The last step is to return a rowCount(). This is the number of records that have been deleted:

    有关完整的代码片段,请参阅代码文件文件夹中的Lesson 6.php文件。

    ```php public function delete($table, $where, $limit = 1) { ksort($where);

    …….. $stmt->execute(); return $stmt->rowCount(); } ```

截断法

  1. 最后一个方法称为 truncate,它接受一个参数$table:
    • $table——表名
  2. Inside the method, call $this->exec and the SQL command TRUNCATE TABLE $table. This will empty the table, resulting in no records. All primary keys will be reset to 0, as if the table had never been used:

    php public function truncate($table) { return $this->exec("TRUNCATE TABLE $table"); }

    整个类如下所示:

    有关完整的代码片段,请参阅代码文件文件夹中的Lesson 6.php文件。

    ```php <?php namespace App\Helpers;

    use PDO; class Database extends PDO { / * @var array Array of saved databases for reusing / / …… { return $this->exec("TRUNCATE TABLE $table"); } } ```

  3. 保存这个类。这是一个复杂的类,我们将要编写的其余代码要简单得多。在接下来的几页中,我们将使用数据库帮助程序,方法的用途和用法将在使用时变得清晰。

基础型号

  1. 我们的下一个任务是创建一个basemodel类,该类将使用数据库助手连接到数据库并返回实例。这将允许其他模型类从此类扩展并使用数据库连接。
  2. 在系统文件夹中创建一个名为BaseModel.php的文件。
  3. 打开 php 并将名称空间设置为 System。
  4. 通过在 use 语句中调用配置类和数据库帮助器类的名称空间来导入它们。
  5. 定义类并调用 BaseModel,然后创建一个名为$db的受保护属性。这是其他模型将用于与数据库交互的内容。
  6. 创建一个__construct()方法。这将在类实例化后立即运行。在这个方法中,创建一个名为$config的局部变量,并将其赋值为Config::get()
  7. Next, create a new instance of the Database helper and call the get method and pass in $config.

    该类如下所示:

    有关完整的代码片段,请参阅代码文件文件夹中的Lesson 6.php文件。

    php <?php namespace System; /* * model - the base model * …….. //connect to PDO here. $this->db = Database::get($config); } }

  8. 现在,我们已经准备好开始使用数据库。在继续我们的代码库之前,请使用 phpmyadmin 或 MySQL 客户端或终端打开您之前连接到的数据库。

  9. Create a database—we will call it mini—and create a table called contacts with two columns: ID and name.

    如果您没有 MySQL 客户端,可以通过键入以下内容使用终端:

    mysql–u 根

    将 root 替换为数据库用户名。Root 是默认值。默认情况下,我已安装 MariaDB。没有密码,但如果需要输入密码,请输入密码标志–p:

    mysql–u 根–p

  10. 创建一个新的数据库。

  11. 创建数据库mini
  12. Select that database:

    php use mini

    现在它使用名为 mini 的数据库。

  13. The database is empty, so let's create a table called contacts:

    php create table contacts ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

    要查看您的表格列表:

    ```php show tables;

    +-----------------+ | Tables_in_mini | +-----------------+ | contacts | +-----------------+ 1 row in set (0.00 sec)

    Insert data into the table insert into contacts (name) values('Dave'); insert into contacts (name) values('Markus');

    this will insert the records to see the contents of the table: select * from contacts;

    +----+--------+ | id | name | +----+--------+ | 1 | Dave | | 2 | Markus | +----+--------+ 2 rows in set (0.00 sec) ```

通过这几个命令,创建了数据库和一个表,该表已填充了两条记录。

活动:创建和执行模型

我们已经创建了联系人控制器并查看了结果。我们现在需要为我们的应用实现该模型。

此活动的目的是为我们的应用实现模型。

回到框架,我们现在准备创建第一个模型。请按照以下步骤进行操作:

  1. Inside the app folder, create a new folder called Models. This is where all the models will be stored. Now, create a new file called Contact.php.

    最佳做法是将模型命名为单个记录,因此在本例中,Contact 表示联系人表。

  2. Contact.php,中打开php并将名称空间设置为App\Models;。

  3. 导入BaseModel并创建名为 Contact 的类,并扩展BaseModel.
  4. 创建一个名为getContacts().的方法此方法将用于获取数据库中存储的所有联系人。
  5. Call $this->db->select() to call the select method of the database helper and write the SQL * FROM contacts.

    最好用大写字母编写像SELECTFROMWHEREGROUP BYORDER BY这样的命令,因此在代码中明确这些命令是什么。

    模型如下所示:

    ```php <?php namespace App\Models; use System\BaseModel;

    class Contact extends BaseModel { public function getContacts() { return $this->db->select('* FROM contacts); } } ```

  6. Now, we need to run this model. The best place for this is inside a controller. Create a new controller called Contacts inside the app\Controllers folder.

    此类从BaseController扩展而来,有一个名为index:的方法

    ```php <?php namespace App\Controllers;

    use System\BaseController;

    class Contacts extends BaseController { public function index() {

    } } ```

    让我们通知index方法加载名为contacts/index的视图:

    php public function index() { return $this->view->render('contacts/index'); }

  7. Create a folder called contacts in app\views and create a file called index.php.

    如果您现在运行此操作并转到localhost:8000/contacts,您将获得一个空白页或看到contacts/index.php的内容,前提是您已经输入了一些内容。

  8. 回到contacts``controller,我们需要导入contact模型。我们通过使用use语句并将名称空间设置为模型

    ```php use App\Models\Contact;

    ```

    来实现这一点 9. 在index方法内部,创建contact模型的新实例并调用getContacts()方法。将其分配给名为$records:

    php $contacts = new Contact(); $records = $contacts->getContacts();

    的变量 10. Next, pass the $records to the view:

    php return $this->view->render('contacts/index', compact('records'));

    使用compact()是一种干净的方式,可以输入表示变量的字符串名称。这将读取$records并将其传递给视图:

    内部 app\views\contacts\index.php

  9. 打开php并检查$records是否存在,然后对每条记录进行foreach循环和循环。echo来自$row对象的name键。添加一个包含<br>标记的字符串,这将导致每个循环位于新行上:

    php <?php if (isset($records)) { foreach ($records as $row) { echo $row->name.'<br>'; } }

  10. 在浏览器中保存并运行并转到http://localhost:8000/contacts。您将在数据库中存储的contacts表中看到联系人列表。

总结

在本章中,我们更好地理解了数据库类在项目中所扮演的角色,开发人员每次与数据库交互时都会用到这个角色。它是 PDO 查询的包装器。他们不需要直接调用它,因为他们是从它扩展而来的。

我们所使用的唯一库是 Whoops,它将以可读的格式显示错误。

我们还获得了构建默认状态的经验,包括baseControllerbaseMethod

在下一章中,我们将构建一个登录系统和身份验证,供用户登录和注销。这将扩展我们到目前为止所涵盖的内容,并引入新概念。在下一章中,我们还将构建密码恢复系统。