设计php框架_利用 Composer 一步一步构建自己的 PHP 框架(三)——设计 MVC

利用 Composer 一步一步构建自己的 PHP 框架(三)——设计 MVC

2014-10-14 / 阅读数:50438 / 分类: PHP

终于可以 “一次编写,到处发布” 了,泪流满面!今天凌晨我从一点多开始搞了四个小时到 5:20,终于搞出了个 Emlog 的 MarkDown 插件!欢迎 Emloger 尝试!

回顾

在上一篇教程中,我们使用 codingbean/macaw 这个 Composer 包构建了两条简单路由,第一条是响应 GET ‘/fuck’ 的,另一条会 hold 住所有请求。其实对 PHP 框架来说,有了路由就有了一切。所以接下来我们要做的事情就是让 MFFC 框架更加规范,更加丰满。

这就牵扯到了 PHP 框架另外的价值:确立开发规范以便于多人协作,使用 ORM、模板引擎 等工具以提高开发效率。

正式开始

规划文件夹

新建 MFFC/app 文件夹,在 app 中创建 controllers、models、views 三个文件夹,开始正式开始踏上 MVC 的征程。

(谁说我抄 Laravel 了,我抄的明明是 Rails :-D)

使用命名空间

新建 controllers/BaseController.php 文件:

/**

* BaseController

*/

class BaseController

{

public function __construct()

{

}

}

新建 controllers/HomeController.php 文件:

/**

* \HomeController

*/

class HomeController extends BaseController

{

public function home()

{

echo "

控制器成功!

";

}

}

增加一条路由: Macaw::get('', 'HomeController@home');,打开浏览器直接访问 http://127.0.0.1:81/,出现以下提示:Fatal error: Class 'HomeController' not found in /Library/WebServer/Documents/wwwroot/MFFC/vendor/codingbean/macaw/Macaw.php on line 93

为什么没找到 HomeController 类?因为我们没有让他自动加载,修改 composer.json 为:{

"require": {

"codingbean/macaw": "dev-master"

},

"autoload": {

"classmap": [

"app/controllers",

"app/models"

]

}

}

运行 composer dump-autoload,稍等片刻,刷新,你将看到以下内容(别忘了调节编码哦~):

设计php框架_利用 Composer 一步一步构建自己的 PHP 框架(三)——设计 MVC_第1张图片

恭喜你,命名空间使用成功!

连接数据库

新建 models/Article.php 文件,内容为(数据库密码请自行更改):

/**

* Article Model

*/

class Article

{

public static function first()

{

$connection = mysql_connect("localhost","root","password");

if (!$connection) {

die('Could not connect: ' . mysql_error());

}

mysql_set_charset("UTF8", $connection);

mysql_select_db("mffc", $connection);

$result = mysql_query("SELECT * FROM articles limit 0,1");

if ($row = mysql_fetch_array($result)) {

echo '

'.$row["title"].'

';

echo '

'.$row["content"].'

';

}

mysql_close($connection);

}

}

修改 controllers/HomeController.php 文件:

/**

* \HomeController

*/

class HomeController extends BaseController

{

public function home()

{

Article::first();

}

}

刷新,这时候会得到 Article 类未找到的信息,因为我们没有更新自动加载配置:composer dump-autoload

在等待的时间里,我们去建立数据库 mffc,在里面建立表 articles,设计两个字段 title、content 用于记录信息,并填充进至少一条数据。你也可以在建立完成 mffc 数据库以后运行以下 SQL 语句:DROP TABLE IF EXISTS `articles`;

CREATE TABLE `articles` (

`id` int(11) unsigned NOT NULL AUTO_INCREMENT,

`title` varchar(255) DEFAULT NULL,

`content` longtext,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

LOCK TABLES `articles` WRITE;

/*!40000 ALTER TABLE `articles` DISABLE KEYS */;

INSERT INTO `articles` (`id`, `title`, `content`)

VALUES

(1,'我是标题','

我是内容呀~~

我真的是内容,不信算了,哼~ O(∩_∩)O

'),

(2,'我是标题','

我是内容呀~~

我真的是内容,不信算了,哼~ O(∩_∩)O

');

/*!40000 ALTER TABLE `articles` ENABLE KEYS */;

UNLOCK TABLES;

然后,刷新!你将看到以下页面:

设计php框架_利用 Composer 一步一步构建自己的 PHP 框架(三)——设计 MVC_第2张图片

恭喜你!MVC 中的 M 和 C 都已经实现!接下来我们开始调用 V (视图)。

调用视图

修改 models/Article.php 为:

/**

* Article Model

*/

class Article

{

public static function first()

{

$connection = mysql_connect("localhost","root","C4F075C4");

if (!$connection) {

die('Could not connect: ' . mysql_error());

}

mysql_set_charset("UTF8", $connection);

mysql_select_db("mffc", $connection);

$result = mysql_query("SELECT * FROM articles limit 0,1");

if ($row = mysql_fetch_array($result)) {

return $row;

}

mysql_close($connection);

}

}

将包含查询结果的数组返回。修改 HomeController:

/**

* \HomeController

*/

class HomeController extends BaseController

{

public function home()

{

$article = Article::first();

require dirname(__FILE__).'/../views/home.php';

}

}

保存,刷新,你将得到跟上面一模一样的页面,视图调用成功!

几乎所有人都是通过学习某个框架来了解 MVC 的,这样可能框架用的很熟,一旦离了框架一个简单的页面都写不了,更不要说自己设计 MVC 架构了,其实这里面也没有那么多门道,原理非常清晰,我说说我的感悟:

1. PHP 框架再牛逼,他也是 PHP,也要遵循 PHP 的运行原理和基本哲学。抓住这一点我们就能很容易地理解很多事情。

2. PHP 做的网站从逻辑上说,跟 php test.php 没有任何区别,都只是一段字符串作为参数传递给 PHP 解释器而已。无非就是复杂的网站会根据 URL 来调用需要运行的文件和代码,然后返回相应的结果。

3. 无论我们看到的是 CodeIgniter 这样 180 个文件组成的“小框架”,还是 Laravel 这样加上 vendor 一共 3700 多个文件的 “大框架”,他们都会在每一个 URL 的驱动下,组装一段可以运行的字符串,传给 PHP 解释器,再把从 PHP 解释器返回的字符串传给访客的浏览器。

4. MVC 是一种逻辑架构,本质上是为了让人脑这样的超低 RAM 的计算机能够制造出远超人脑 RAM 的大型软件,其实 MVC 架构在 GUI 软件出现以前就已经成形,命令行输出也是视图嘛。

5. 在 MFFC 里,一个 URL 驱动框架做的事情基本是这样的:入口文件 require 控制器,控制器 require 模型,模型和数据库交互得到数据返回给控制器,控制器再 require 视图,把数据填充进视图,返回给访客,流程结束。

WRITTEN BY

9918749bb4d134e0a0a19c7c9d51aff8.png

程序员,Swift Contributor,正在写《iOS 可视化编程与 Auto Layout》。

评论:

3952c5af910d3fb96758ff34cd8f4b9c.png

笑的很甜丶

2019-08-30 16:32

UP主,我发现你已经把改项目发展为了一个成熟的框架Pinatra。我也有下载下来体验。问一个比较简单的问题,我想实现路由不加Macaw::前缀有什么办法吗?恕我没有参透Pinatra。谢谢 ~_~ ~!

17795660dc39be85b37e2e2a4b81fd6f.png

xy0318

2018-05-30 16:39

实现psr4自动加载类

首先

composer.json中添加

"autoload": {

"psr-4": {

"App\\": "app/"

}

}

"App\\": "app/" 表示命名空间的App 映射到目录 app下

然后 删掉刚才的classmap

composer update

在你的 model 和 controller 里声明命名空间

Article.php

namespace App\models;

HomeController.php

namespace App\controllers;

use App\model\Article;

最后修改你的路由

Macaw::get('fuck', 'App\controllers\HomeController@index');

17795660dc39be85b37e2e2a4b81fd6f.png

陈立

2018-04-28 16:36

很是奇怪

Macaw::get('', 'Index@index');

autoload_classmap.php 文件的内容是这样

$vendorDir = dirname(dirname(__FILE__));

$baseDir = dirname($vendorDir);

return array(

'app\\home\\controller\\Base' => $baseDir . '/app/home/controller/Base.php',

'app\\home\\controller\\Index' => $baseDir . '/app/home/controller/Index.php',

);

但是就是报错找不到类,

Macaw::get('', 'app\home\controller\Index@index');

加上命名空间就可以

composer.json是这样的

{

"require": {

"codingbean/macaw": "dev-master"

},

"autoload": {

"classmap": [

"app/home/controller",

"app/home/model"

]

}

}

3952c5af910d3fb96758ff34cd8f4b9c.png

笑的很甜丶

2019-08-30 16:10

@陈立:赞同,向laravel更进一步,哈哈。

1ab6f1488feac548cf4ca75272f788f3.gif

1ab6f1488feac548cf4ca75272f788f3.gif

1ab6f1488feac548cf4ca75272f788f3.gif

1ab6f1488feac548cf4ca75272f788f3.gif

1ab6f1488feac548cf4ca75272f788f3.gif

1ab6f1488feac548cf4ca75272f788f3.gif

1ab6f1488feac548cf4ca75272f788f3.gif

1ab6f1488feac548cf4ca75272f788f3.gif

1ab6f1488feac548cf4ca75272f788f3.gif

1ab6f1488feac548cf4ca75272f788f3.gif

1ab6f1488feac548cf4ca75272f788f3.gif

16

2017-08-16 15:57

a0542f03c517b09fdc8eee2307b1976a.gif

未匹配到路由

控制器创建成功!

两个同时出现 开始127.0.0.1/index.php/fuck是成功的

hollow

2017-09-14 22:53

@16:你好,你的views/home.php是怎么写的呢,怎么从控制器层将$article传到视图层的呢

17795660dc39be85b37e2e2a4b81fd6f.png

hollow

2017-09-15 16:37

@hollow:已经明白了

17795660dc39be85b37e2e2a4b81fd6f.png

杀不死

2017-07-13 10:08

少了个 home.php 。

在home.php怎么获取数据去渲染页面呢

17795660dc39be85b37e2e2a4b81fd6f.png

5xjian

2017-10-10 13:37

@杀不死:

echo '

'.$article["title"].'

';

echo '

'.$article["content"].'

';

这个时候还没有说到模板数据渲染,直接的php原生可以解决

17795660dc39be85b37e2e2a4b81fd6f.png

Alex-liu

2017-05-20 13:51

博主您好,看了您的文章获益良多。  但是在使用过程中遇到了一个问题。

1. 数据库迁移。

Laravel Eloquent 的教程,我看了。迁移脚步是基于 Laravel 的。有没有好的推荐呢?

2. 如何引入 JS、CSS 文件?

因为设置了伪静态、所有的地址都会指向 index.php,  但是路由又把请求 js、css 的拦截了。因为你的例子里以及 Github 上都没有引入的事例都没有引入的 js、css,所以有点迷茫。

---------.htaccess 的配置

#开启URL重写

RewriteEngine On

#RewriteBase /

#!-d 请求内容不是目录

RewriteCond %{REQUEST_FILENAME} !-d

#!-f 请求内容不是文件

RewriteCond %{REQUEST_FILENAME} !-f

RewriteRule ^(.*)/$ /$1 [L,R=301]

RewriteRule ^ index.php [L] #[L] 这是最后一个匹配项,不再往下匹配

---------routes.php

//PAGE

Macaw::get('/',"App\\Module\\Management\\controllers\\LoginController@index");

Macaw::get('index',"App\\Module\\Management\\controllers\\LoginController@index");

Macaw::get("login","App\\Module\\Management\\controllers\\LoginController@login");

还请您帮忙解答下。

17795660dc39be85b37e2e2a4b81fd6f.png

回眸

2018-01-31 14:44

@Alex-liu:location / {

index  index.php index.html index.htm;

#如果请求既不是一个文件,也不是一个目录,则执行一下重写规则

if (!-e $request_filename)

{

#地址作为将参数rewrite到index.php上。

rewrite ^/(.*)$ /index.php?s=$1;

#若是子目录则使用下面这句,将subdir改成目录名称即可。

#rewrite ^/subdir/(.*)$ /subdir/index.php?s=$1;

}

}

65f5da31654ce2458699b40573006e09.png

拉风的男人

2017-04-12 15:53

讲师您好:我现在的在使用Article::first() 有没有办法能调用到其他目录的model呢,

结构如下

App

public

model

Article.php

Home

controller

HomeController.php

谢谢

17795660dc39be85b37e2e2a4b81fd6f.png

你的评论模块

2017-05-01 15:26

@拉风的男人:

c3ce0667366e4d9d32653112402939df.gif你的评论模块怎么实现的

典到为止

2017-04-05 22:14

受益,谢谢。

17795660dc39be85b37e2e2a4b81fd6f.png

菜鸟

2016-12-25 15:44

mysql_connect  已经没弃用了咋办

17795660dc39be85b37e2e2a4b81fd6f.png

mrhopelee

2017-02-06 18:52

@菜鸟:改为使用mysqli_connect

class Article

{

public static function first()

{

$connection = mysqli_connect("localhost","root","3355");

if (!$connection) {

die('Could not connect: ' . mysqli_connect_error());

}

mysqli_set_charset($connection, "UTF8");

mysqli_select_db($connection, "mffc");

$result = mysqli_query($connection, "SELECT * FROM articles limit 0,1");

if ($row = mysqli_fetch_array($result)) {

return $row;

}

mysqli_close($connection);

}

}

3952c5af910d3fb96758ff34cd8f4b9c.png

笑的很甜丶

2019-08-30 16:24

@mrhopelee:早知道回复中有代码,我就不去搜索了。正如文中所述,框架用的时间太长了,一些基本的概念都会变得陌生。

17795660dc39be85b37e2e2a4b81fd6f.png

宇多一

2016-12-07 16:21

HomeController是如何传递到视图的?

也就是home.php如何打印输出呢?

gong

2016-12-06 01:11

您好链接数据库操作为什么不适用PDO那?是mysql_connect比PDO有什么优秀的地方吗?还是比较方便?谢谢

suc

2016-11-04 14:30

"autoload": {

"psr-4": {

"App\\Controllers\\": "app/Controllers"

}

}

使用这样的方式为何还是提示HomeController找不到了;求指点;如何需要不用每次dump 有啥具体做法thx;因为自己查了下质料木有搞定;

cd9254147f2d9380fb749cb8d1fdb71e.gif

cd9254147f2d9380fb749cb8d1fdb71e.gif

cd9254147f2d9380fb749cb8d1fdb71e.gif

17795660dc39be85b37e2e2a4b81fd6f.png

菜鸟

2016-12-24 22:57

@suc:"App\\Controllers\\": "App/Controllers/" 写成这样

gbasp2

2016-10-12 10:50

为什么路由要这样设置呢 这样不是没添加一个controller,function 就要添加一条路由?

LaravelChen

2017-03-05 12:21

@gbasp2:你可能只接触过tp,或者ci之内的,没接触过laravel这些框架,所以你很疑惑!

43e2aee7ee94ca4aa83e70f1cc1674ad.gif

nil

2016-08-24 00:54

HomeController是如何传递到视图的?

也就是home.php如何打印输出呢?

17795660dc39be85b37e2e2a4b81fd6f.png

小杨

2015-11-25 11:13

00c9d8e8322de036b66bb9a5abd1d07a.gif,不过我试试了用 psr4 的方式自动加载 Controller,好像不支持啊??还是我自已经没搞对呢,求大神赐教

b9b357d6852de4aff80f7e731f6867c8.gif

17795660dc39be85b37e2e2a4b81fd6f.png

圣骑

2016-07-28 16:09

@小杨:

cd9254147f2d9380fb749cb8d1fdb71e.gif我的也不行,每次新增一个控制器就要dump-autoload一次

17795660dc39be85b37e2e2a4b81fd6f.png

圣骑

2016-08-31 16:17

@圣骑:

c3ce0667366e4d9d32653112402939df.gif解决了

suc

2016-11-04 14:08

@圣骑:如何解决的了可否分享;thx

Jceee

2015-08-09 21:50

博主你好,我在/MFFC/app/controller/下建了2个文件夹,一个Home和一个Test,

两个文件夹中都创建了一个IndexController.php控制器文件,类名一样都是IndexController

然后用composer.phar dump-autoload 重新生成autoload_classmap.php文件时,发现了:

return array(

'BaseController' => $baseDir . '/app/controller/BaseController.php',

'BaseModel' => $baseDir . '/app/models/BaseModel.php',

'HomeController' => $baseDir . '/app/controller/HomeController.php',

'IndexController' => $baseDir . '/app/controller/Home/IndexController.php',

);

IndexController这个类名的自动加载路径是$baseDir . '/app/controller/Home/IndexController.php',  Home文件夹下的。

这样的话,加载Test下的文件时会有问题,好吧....试了一下加上命名空间,重新composer.phar dump-autoload就可以了

'Controller\\Test\\IndexController' => $baseDir . '/app/controller/Test/IndexController.php',   //命名空间的IndexController

'IndexController' => $baseDir . '/app/controller/Home/IndexController.php',

这种有命名空间的类在routes中需要怎么指向调用?

f2eb4aff4d2bcce79de0c4ced545de54.png

2015-08-09 23:32

@Jceee:像 Laravel 那样调用。

加上绝对命名空间的类名才是一个类真实、完整的类名。

17795660dc39be85b37e2e2a4b81fd6f.png

gflhx

2015-05-14 11:40

为什么我的总是出现Fatal error: Class 'HomeController' not found in /www/mffc/vendor/noahbuscher/macaw/Macaw.php on line 94,已经composer dump-autoload

Generating autoload files

f2eb4aff4d2bcce79de0c4ced545de54.png

2015-05-14 11:56

@gflhx:composer.json 中的自动加载没有配置好

17795660dc39be85b37e2e2a4b81fd6f.png

winter

2016-04-30 21:07

@JohnLui:我的也是这样

17795660dc39be85b37e2e2a4b81fd6f.png

xiaochuan

2015-04-16 18:48

为啥博主还是用的mysql_*,不都换成mysqli_*或者用PDO呢? mysql的不是早就已经被deprecated了,不推荐使用了吗?

f2eb4aff4d2bcce79de0c4ced545de54.png

2015-04-16 19:49

@xiaochuan:暂时的~往下看嘛

2015-03-26 17:04

报一个遇到的错误!

Macaw::get('', 'HomeController@home')

这样注册路由也会报文件不存在!

应该把命名空间也加上

Macaw::get('', 'controllers\HomeController@home')

这样就不会错!

因为 autoload_classmap.php 里面只有 “controllers\HomeController” 键 的记录,没有 “HomeController” 键!

我反正是碰到这样的错误了... 谢谢博主的教程...爽歪歪!

f2eb4aff4d2bcce79de0c4ced545de54.png

2015-03-26 17:46

@hiicup:MFFC 的控制器和 Model 的命名空间都是顶级命名空间,采用psr-0 自动加载,你应该是做了特殊的设置。

17795660dc39be85b37e2e2a4b81fd6f.png

Sage

2016-10-11 15:00

@JohnLui:

43e2aee7ee94ca4aa83e70f1cc1674ad.gif完全按照你的来做的,但是就是总报文件不存在,加上命名空间才可以

17795660dc39be85b37e2e2a4b81fd6f.png

LAU

2015-01-17 15:25

如何跳转到另一个页面,例如我想要提交form表单,那如何form的action里填写链接?

f2eb4aff4d2bcce79de0c4ced545de54.png

2015-01-17 21:08

@LAU:

b9b357d6852de4aff80f7e731f6867c8.gif

b9b357d6852de4aff80f7e731f6867c8.gif直接写哇~ 'article/1/update'。

17795660dc39be85b37e2e2a4b81fd6f.png

LAU

2015-01-18 17:54

@JohnLui:这代表什么意思

1 2

发表评论:

昵称

邮件地址 (选填)

个人主页 (选填)

你可能感兴趣的:(设计php框架)