人人都能看懂的全栈开发教程——创建 Symfony 命令

人人都能看懂的全栈开发教程——创建 Symfony 命令

Chris Yue No Comment
Posts

前面几篇文章已经给读者展示了几个 Symfony 自带的命令,而本篇文章的目的就是让大家了解,如何在 Symfony 里写一个自己的命令,比如我们老项目里的用户注册命令。

还记得我们创建控制器时说可以直接通过命令 make:controller 来自动生成代码吗?命令本身也跟控制器类似,我们可以自己创建代码,也可以选择通过命令帮我们创建,这一次,我们直接选择命令生成的方式。

我们先执行命令:

运行之后会提示你输入你想创建的命令的名称。我们就按照 Symfony 的风格,将命令命名为 app:user:register。回车之后,会提示创建了 src/Command/UserRegisterCommand.php 文件。

实际上这个时候,大家就可以通过 bin/console app:user:register 来运行我们刚刚创建的命令了,当然,只是给我们展示了一个例子,我们依然需要修改命令文件的代码来实现我们注册用户的需求:

当然,目前这个命令依然不能使用,因为 UserManager 需要的 UserRepositoryPasswordEncoder 目前还没有。

我们先把老项目里的 DefaultPasswordEncoder 类文件复制到新项目的 src/Security 目录里(如果没有 Security 目录就先创建)。因为放的位置变了,类的命名空间也需要更新一下:

接下来是 UserRepository 的迁移,按理说,我们也应该将老项目里的 UserRepository 直接复制到新项目里,然后将原来的 PDO 改成用 Doctrine 来获取用户就行。不过为了给大家展示更多的内容,我打算换一种方式。

Symfony 框架在用户登录和权限方面,其实做了很多事情。Symfony 的用户系统会要求开发者创建用户对象,并且用户对象需要能返回加密密码,返回用户角色等功能,但这些功能具体应该如何做,是开发者自己来定的,并不属于框架要负责的事情(不过 Symfony 也提供了一个例子 Symfony\Component\Security\Core\User\User 类,可以参考一下),所以 Symfony 提供了一个用户接口来让使用 Symfony 框架的开发者来实现,这个思路跟我们在老项目里创建 UserRepositoryInterfacePasswordEncoderInterface 接口定义其实是一样的。这个用户接口叫 Symfony\Component\Security\Core\User\UserInterface,读者如果有兴趣,可以自己去看看这个接口都定义了些什么方法,如何在库里找到指定的类源文件这里就不说了,大家可以试着找一下,没那么困难。

总之为了后面我们做用户登录的功能,我们需要实现用户接口。实现这个接口是因为我们用了 Symfony 框架导致的,即跟具体做法有关,而跟业务没关系,所以我们不应该在 Domain\User 上去实现这些接口,不过我们可以新创建一个用户类,继承我们的 Domain\User,并且实现 Symfony 的 UserInterface 里的方法。

我们可以自己创建新用户类,也可以通过 Symfony 的命令来为我们创建一些便利。我们只需要执行下面命令:

命令会提出几个问题,依次是:

The name of the security user class (e.g. User) [User]

即用户类名。结尾中括号里面的是默认值,这里就直接回车接受默认值就行。

Do you want to store user data in the database (via Doctrine)? (yes/no) [yes]

是否用 Doctrine 来保存用户数据,也默认值即可。

Enter a property name that will be the unique “display” name for the user (e.g. email, username, uuid) [email]:

用来表示用户唯一性的字段,比如说 email,或者用户名,或者 uuid,我们的项目目前只有用户名,所以回答 username

Does this app need to hash/check user passwords? (yes/no) [yes]

Symfony 的登录系统实际上自带密码加密和验密的功能,这里便是问你需不需要加密验密功能。因为我们自己已经提供,所以回答 no

最后可以看到命令运行的反馈,已经为我们创建好了用户类和用户仓库类(UserRepository),不过我们还是需要做一些修改来满足我们的需求。

首先来看 src/Entity/User.php。这个类里出现了很多 @ 开头的注释,这些就是使用 annotation 来定义数据库表映射的方式,大家可以参考一下,不过最终我们需要将代码改成这样:

光改 App\Entity\User 类还不够,我们依然要告诉 Doctrine,App\Entity\User 也是一个需要存库的实体类。我们在 config/doctrine/mappings 目录创建一个新的目录 app,并且创建文件 config/doctrine/mappings/app/User.orm.xml 并写下以下配置:

以及告诉 Doctrine 要去什么地方找这些配置:

因为原来的 Domain\Entity\User 类已经不是我们最终用的实体了,所以我们需要告诉 Doctrine Domain\Entity\User 是一个『被映射的父类』(mapped-superclass):

还记得前一篇我有提到,如果实体一些属性,或者方法,并不是业务要求的,而是具体实现的时候要求的,应该怎么处理,是的,使用实体继承,就是一种方式,你可以将具体实现所要求的属性或者方法,添加到子类里就行。而因为子类就『是』父类(猫是哺乳动物的子类,猫也就『是』哺乳动物),所以代码在实际运行中虽然用户数据是子类对象,但依然没毛病,业务代码也能处理。

接下来我们需要修改 src/Repository/UserRepository.php 文件:

还记得在老项目里,我们是在注册用户命令里面,先自己创建了 UserRepositoryDefaultPasswordEncoder 对象之后,才创建的 UserManager 对象吗?在 Symfony 里这个过程还能更简单,只要在 config/services.yaml 文件里添加几行配置:

这个时候,Symfony 框架自己知道我们创建的命令 App\Command\UserRegisterCommand 的构造函数需要 Domain\UserManager 对象作为参数(有一种更『专业』的说法叫做『注入』,后面我们都这么说,另外关于注入后面会有专门的文章介绍),并且尝试创建 Domain\UserManager 对象,这个时候 Symfony 框架又会发现 Domain\UserManager 又需要 Domain\UserRepositoryInterfaceDomain\PasswordEncoderInterface,而 Symfony 也知道当前项目下实现了这俩接口的类也只有 App\Security\DefaultPasswordEncoderApp\Repository\UserRepository,所以就会自动创建这两个类…… Symfony 在很多方面,都会用这么一点我管它叫『小智能』的东西,来为开发者提供一些方便,节省没必要的代码量,这也是使用 Symfony 或者说使用框架(其他的框架或多或少也有,或者将会有类似的功能)的好处。

到这一步,我们再执行注册用户命令做一下尝试:

如果一切正常,将会出现注册成功的提示。

另外我们的 domain 代码自带重复用户名检查,我们尝试再次执行上面的命令而且不改用户名,我们应该能看到红色的报错信息。

最后我想说的是,把 Symfony 创建命令的功能拿出来单独说一下的意义是,Symfony 框架有很多功能,都是由 Symfony 组件(Components)作为核心提供的,而 Symfony 命令行组件(Symfony Console Component)是其中最有名的组件之一,很多 PHP 项目,甚至其他的 PHP 框架,命令行功能都是由 Symfony 命令行组件提供的。你以后的工作中即使使用的是别的框架,但依然很有可能会『遇上熟人』;另外如果你自己的项目也需要大量的命令,也可以单独使用 Symfony 命令行组件。

本章完整代码见这里

人人都能看懂的全栈开发教程——创建 Symfony 命令 by Chris Yue is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

微信赞赏码

如果觉得文章还不错,就请扫码鼓励一下作者吧
天使打赏人

发表评论

86 − = 78