资讯专栏INFORMATION COLUMN

Symfony2中创建数据模型

rainyang / 1410人阅读

摘要:今天我在这里给大家分享一下在中如何创建数据模型和基于的搭建。对象与数据库的关系通常在电脑的世界里创建的数据模型会转换为数据库里的一条条记录。

Symfony是一款优秀的基于MVC架构的PHP框架。今天我在这里给大家分享一下在Symfony中如何创建数据模型和基于RESTful api的搭建。重点是如何创建数据模型哦!

本教程使用的当前Symfony的LTS版本(Symfony 2.8)

本教程开始学做日期为2016年11月

如何安装Symfony?

同学们自己去官网看啦 -- Symfony官方网站。无论是使用Symfony installer还是Composer,在中国大陆地区下载速度会比较慢,需要使用科学的上网方法。

何为MVC?

MVC这一点我得重点说一下。去任何一家企业面试PHP,90%的同学会被问到什么是MVC。我相信大家已经背得滚瓜烂熟,可以自信满满的回答道:MVC是三个单词的首字母缩写,它们是Model(模型)、View(视图)和Controller(控制器)。Ok,面试官满意地点点头,进入下一个问题。但是,但是!在实际工作中,我发现很多同学在MVC框架下依然喜欢使用手写sql语句进行数据库操作。从这点来看,我个人认为这样做的同学其实是没有理解到MVC的,下面我就讲一讲我对MVC的理解。

请大家想象下图所描绘的场景:Peter和John相隔千里,但他们可以通过互联网进行沟通。抽象一点理解,Peter和John是在电脑的世界里面进行沟通。在电脑的世界里面,不再有现实中距离的限制,千里之外瞬息即到!模型就是现实对象在计算机世界里面的抽象,我们将Peter和John进行了建模,让他们能够进入计算机的世界。通过controller我们可以操作计算机世界里的数据模型,通过view我们可以接收计算机世界里数据模型传达的信息。重点:模型是计算机世界里实实在在的一个对象(Object),模型是现实世界里一个对象在计算机世界里的抽象。

对象与数据库的关系

通常在电脑的世界里创建的数据模型会转换为数据库里的一条条记录。例如,我们创建了Peter和John这两个数据模型,那么在数据库的User表里面就会多出下面这两条记录:

| id | name  | gender | age |
| 1  | Peter | male   | 30  |
| 2  | John  | male   | 25  |

那么在MVC构架的框架里,这两个数据模型写入数据库的过程通常是这样的:

首先,定义个一个User类(以下代码为伪代码)

Class User {
    public $name;
    public $gender;
    public $age;
    
    # 下面会有一堆getter和setter
}

然后实例化两个User类

$peter = new User();
$peter->name = "Peter";
$peter->gender = "male";
$peter->age = 30;
$peter->save(); // 将Peter存入数据库

$john = new User();
$john->name = "John";
$john->gender = "male";
$john->age = 25;
$john->save(); // 将John存入数据库

重点来了,有些同学会说:你这样太麻烦了,两句sql不就搞定了吗?!具体sql语句如下:

INSERT INTO User VALUES ("Peter", "male", 30)
INSERT INTO User VALUES ("John", "male", 25)

然而,在实际项目当中,数据模型不可能像我刚才说的例子那么简单,若使用手写sql的话,就必须要再写代码实现记录到逻辑的转换,逻辑复杂了,工作量就会很大!但是,如果让你手动创建数据模型,往往你又会觉得也是一件麻烦的事情。在有了Symfony之后,数据模型的创建就变得无比简单,接下来我会用实际的例子告诉你怎么在Symfony里面快捷高效的创建数据模型!

设计数据模型

好了,假设我们现在要为一所学校做一个系统,具体需求如下图所示:

老师A有两门课 - 课程A和课程B,学生A和学生B选修了课程A,学生B和学生C选修了课程B。拿到这个需求的时候,千万不要急急忙忙的开始写代码,首先要做的是理清楚老师,课程还有学生之间的逻辑关系,建好数据模型。因为在逻辑都还没有理清的情况下写出来的代码你觉得会没有问题吗?!到了那时,你会不停的进行结构大改,把自己搞得筋疲力尽而且代码写的乱七八糟!

好,在了解需求之后,我们总结出以下两点:

一个老师可以教授多门课程,但一门课程只能有一个授课老师,即老师和课程是一对多的关系,老师需要保存的字段为:姓名,性别,年龄,院系,职位,发表论文数量,课程需要保存的字段为:课程代码,课程名称,课程开始时间,课程结束时间。

一门课程可以被多个学生选修,一个学生也可以选修多门课程,即学生和课程是多对多的关系。学生需要保存的字段为:姓名,性别,年龄,学院,年级。

逻辑关系理清楚了,就可以开始数据建模了。我所谓的数据模型其实很简单,就是类和类与类之间的关系的图表,根据以上需求,我们可以创建如下对应的数据模型:

上面设计的数据模型是可用的,但是我们发现一个问题 - 老师和学生有相同的字段,例如姓名,性别等,因为这些都是一个人基本的属性。那么运用OOP的思想,我们眼前豁然开朗 - 老师和学生其实都是继承了人这个对象。基于这点,我们可以优化之前的数据模型,让逻辑更加清晰,数据库表里的字段不再有冗余!优化后的数据模型如下图所示:

到这里,数据模型已经搭建完成,业务逻辑已经整理完毕。可以说已经完成了大半了工作。有的同学会说:你搞了那么半天,一点代码都没写,没有一点产量。那我只能回答:呵呵。下面我就实际演示一下什么是基于框架下的快速应用开发(RAD),希望同学们可以明白咱们程序员一定要取巧,想比做更加重要,不然你一辈子都是码农,干十年等于积累一年经验!

创建数据模型

进入symfony2的根目录,执行如下命令:

php app/console 

然后就会列出Symfony2所有的可用命令,我们重点关注以下两条命令:

generate:bundle: 创建一个bunlde,你可以把Bundle理解成一个模块 - 处理同一个问题的程序和资源的一个集合。Bundle和Bundle之间应该是低耦合(最好是零耦合),这一点应该很容易理解吧。例如一个是前端Bundle一个是后端Bundle,你要是在前端Bunlde里写了一堆处理后端逻辑的程序,在后端Bundle里加了一些处理前端逻辑的程序。Ok,我建议你应该先去重新理解一下编程的思想。为什么说可以做到零耦合呢?因为在Symfony2里面一些多个Bundle都会调用的方法可以注册成Service,而不需要直接到方法所在的Bundle里去调用。

doctrine:generate:entity: 生成一个模型,entity就是实体模型的意思。

首先使用 php app/console generate:bundle 生成一个名为Backend的Bundle,生成步骤如下图:

接着,使用 php app/console doctrine:generate:entity 生成数据模型,根据提示来一步一步来,很简单的!例如下图演示了如何生成学生数据模型:

进行如上操作之后,可以发现在项目目录结构下已经多了一个叫BackendBundle的文件夹,并且4个数据模型已经建好了,如下图所示:

接着,我们需要手动调整一下Symfony2帮我们自动生成的这4个数据模型。

数据表属性命名规范

在Symfony2 entity类中,属性名若由多个单词组成,一般使用大写字母分隔,例如:finishAt。而在数据表中,属性名若由多个单词组成,一般使用下划线分隔,例如:finish_at。查看自动生成的数据模型的属性,具体格式如下:

/**
 * @var DateTime
 *
 * @ORMColumn(name="startFrom", type="datetime")
 */
 private $startFrom;

可以发现,每一个属性上面都有一堆注释,但这一堆注释不同于普通的注释,因为它其实就是一段程序,因为我们在生成BackendBundle的时候Configuration format选择的是annotation。在Symfony2中,路由注册,变量类型定义,数据库属性定义等都可以通过annotation的方式。特别是在路由注册的时候,我特别偏好于annotation的方式,因为它是离散型的,就像一个标签一样贴在了所作用属性或控制器的上面。有其他框架开发经验的同学们应该知道在其他框架中,注册的路由通常会被写在一个文件里面,比如在Yii2 basic中,所有注册路由都在config/web.php的urlManager中。我曾经接手了一个基于Yii2 basic的项目,一打开路由配置文件,发现里面有一千多行注册路由的代码,而且之前注册这些路由的程序员根本就没正确理解路由的注册,搞得配置文件乱七八糟,每次打开这个文件心里就会有一种很不爽的感觉,程序员的洁癖呀!还有一点就是,在团队合作中,这种中心配置文件肯定会被不同的程序员编辑,合并时发生冲突的可能大大增加,如果是Git还好一点,但如果你还用SVN,你就等着哭吧。好了,不吐槽了,我简单讲解一下上面的那一段代码,@var定义了$startFrom为DateTime类型,@ORMColumn定义了数据表中,属性名为startFrom,类型为datetime。我们只需要修改一下数据表中的属性名就可以统一数据库表属性命名规范:

/**
 * @var DateTime
 *
 * @ORMColumn(name="start_from", type="datetime")
 */
 private $startFrom;
数据模型验证

验证就是指检查传递过来的数据是否正确,例如之前的startFrom是一个DateTime类型,如果传递一个string类型过来,肯定是不正确的。在Symfony2中,在每个属性的annotation就可以定义验证条件,详情可见 Symfony Validation。下面举两个例子说明一下:

/**
 * @var string
 *
 * 不能为空
 * @AssertNotBlank()
 * 值必须为Male或Female
 * @AssertChoice({"Male", "Female"})
 *
 * @ORMColumn(name="gender", type="string", length=255)
 */
 private $gender;
/**
 * @var string
 *
 * 不能为空
 * @AssertNotBlank()
 * 值类型需为string型
 * @AssertType("string")
 *
 * @ORMColumn(name="name", type="string", length=255)
 */
 private $name;
自动时间戳属性

在数据模型中,一般我们会默认加入两个时间戳属性 - createdAt(创建于)和updatedAt(更新于),这两个属性显然不应该是由我们传值的,而是在记录生成和更新的时候自动将当时的时间存下来。对于这个问题,StofDoctrineExtensionsBundle这个第三方Bundle已经给我们提供了完美的解决方案。

提示:为什么我们喜欢用流行的第三方框架,应为流行的第三方框架有自己的社区,有一大帮程序员不停的在为社区的壮大献出自己的一份力。很多时候,很多问题其实都是别人已经解决过了的。所以,我们在遇到一个问题的时候,要多去社区里面找找成熟的解决方案,而不是关上门重复的造轮子。

Symfony官方帮我们总结出了最火的30个第三方Bundle,有兴趣的可以看一下,说不定会有惊喜哦!

首先,用Composer下载这个第三方Bundle:

composer require stof/doctrine-extensions-bundle

然后,启用和配置Bundle:

// app/AppKernel.php

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // ...

            new StofDoctrineExtensionsBundleStofDoctrineExtensionsBundle(),
        );

        // ...
    }

    // ...
}
// app/config/config.yml
stof_doctrine_extensions:
    default_locale: en_US
    orm:
        default:
            timestampable: true

最后,在createdAt和updatedAt属性上面用Annotation的方法进行配置即可:

use GedmoMappingAnnotation as Gedmo;

// ...

/**
 * @var DateTime
 *
 * @GedmoTimestampable(on="create")
 * @ORMColumn(name="created_at", type="datetime")
 */
 private $createdAt;

/**
 * @var DateTime
 *
 * @GedmoTimestampable(on="update")
 * @ORMColumn(name="updated_at", type="datetime")
 */
 private $updatedAt;
建立模型与模型之间的关系

之前我们已经总结出了数据模型之间的关系,现在让我们再回顾一下:

Teacher与Course的关系为一对多

Student与Course的关系为多对多

Teacher和Student都继承Person类

具体实现如下:

// src/BackendBundle/Entity/Course.php

use DoctrineCommonCollectionsArrayCollection;

// ...
class Course {
  // ...

  /**
   * @var Teacher
   *
   * @ORMManyToOne(targetEntity="Teacher", inversedBy="courses")
   * @ORMJoinColumn(name="teacher_id", referencedColumnName="id")
   */
  private $teacher;

  /**
   * @ORMManyToMany(targetEntity="Student", inversedBy="courses")
   * @ORMJoinTable(name="course_student")
   */
  private $students;

  public function __construct()
  {
    $this->students = new ArrayCollection();
  }

  // ...
}
// src/BackendBundle/Entity/Teacher.php

use DoctrineCommonCollectionsArrayCollection;

// ...
class Teacher extends Person {

  // ...
  
  /**
   * @ORMOneToMany(targetEntity="Course", mappedBy="teacher")
   */
  private $courses;

  public function __construct()
  {
    $this->courses = new ArrayCollection();
  }

  // ...
}
// src/BackendBundle/Entity/Student.php

use DoctrineCommonCollectionsArrayCollection;

// ...
class Student extends Person {

  // ...
  
  /**
   * @ORMManyToMany(targetEntity="Course", mappedBy="students")
   */
  private $courses;

  public function __construct()
  {
    $this->courses = new ArrayCollection();
  }

  // ...
}
// src/BackendBundle/Entity/Person.php

/**
 * Person
 *
 * @ORMTable(name="person")
 * @ORMEntity(repositoryClass="BackendBundleRepositoryPersonRepository")
 * @ORMInheritanceType("JOINED")
 */
abstract class Person
{
  // ...
}

然后运行php app/console doctrine:generate:entities BackendBundle生成刚才我们加入的自定义属性的getter和setter。之后,运行php app/console doctrine:schema:update --force,数据表就已经成功生成了,赶快去数据库里面看看吧。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/22004.html

相关文章

  • 让Vagrant支持在Windows的同步共享文件中创软链接

    摘要:第三步,测试是否可以创建软链接不过硬链接是不行的谁知道怎么实现,还好大部分项目都不需要用到硬链接第四步,测试创建个项目附上文本形式的配置 samba在windows下居然比nfs快很多倍(这太不科学了,windows的nfs是不是故意做得这么垃圾),而且非常稳定,推荐用samba代替nfs,samba解决了开发环境问题,哈哈哈哈哈哈哈,2015年11月2日 19:43:42 updat...

    Snailclimb 评论0 收藏0
  • PHP路由性能测试

    摘要:路由控制器性能测试前言前段时间抽空写了个微型路由控制器可以在我的另一篇文章里面看到详细的介绍。上门有测试以及这几个路由控制器的性能,总体来说和在最坏的情况下表现最好。同样的测试条件。所以本次测试仅仅针对,以及。 PHP路由控制器性能测试 前言 前段时间抽空写了个微型路由控制器可以在我的另一篇文章里面看到详细的介绍。虽然在设计的时候及尽量避开了正则匹配和数组循环这种耗时的操作。尽量节省时...

    levy9527 评论0 收藏0
  • Linux 安装 Symfony2.8

    摘要:环境说明操作系统安装准备均使用安装至少要有个的环境是少不了了安装步骤下载官方命令工具创建项目这里执行项目创建时,会从官网下载源码包,执行完后就能在当前目录看到了这里我创建了一个新的项目叫,最后的不是项目名字中的是要下载指定的版本的源 环境说明 操作系统 tony@ubuntu:~$ lsb_release -a No LSB modules are available. Distrib...

    Yang_River 评论0 收藏0
  • Symfony2.8 源码分析之类的加载

    摘要:今天来写写这个框架的类加载机制版本原理在项目启动时,通过注册了要使用的类的自动加载处理方法,在类第一次被使用的时候,类文件通过该方法被引入,然后类才得以使用源码分析在的入口文件,我们找到我们随着这个路径我们找打了这个主要内容如下其中是为了注 今天来写写Symfony2.8 这个框架的类加载机制 版本 Symfony 2.8 原理 在项目启动时,Symfony 通过spl_autoloa...

    Blackjun 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<