Laravel學(xué)習(xí)筆記之Artisan命令生成自定義模板的方法
本文主要講述Laravel的Artisan命令來(lái)實(shí)現(xiàn)自定義模板,就如經(jīng)常輸入的php artisan make:controller ShopController就會(huì)自動(dòng)生成一個(gè)ShopController.php模板文件一樣,通過(guò)命令生...
本文主要講述Laravel的Artisan命令來(lái)實(shí)現(xiàn)自定義模板,就如經(jīng)常輸入的php artisan make:controller ShopController
就會(huì)自動(dòng)生成一個(gè)ShopController.php
模板文件一樣,通過(guò)命令生成模板也會(huì)提高開(kāi)發(fā)效率。同時(shí),作者會(huì)將開(kāi)發(fā)過(guò)程中的一些截圖和代碼黏上去,提高閱讀效率。
備注:個(gè)人平時(shí)在寫(xiě)Repository代碼時(shí)會(huì)這樣寫(xiě),如先寫(xiě)上ShopRepositoryInterface并定義好接口方法如all()
、create()
、update()
、delete()
、findBy()
等等,然后再寫(xiě)上接口對(duì)應(yīng)的實(shí)現(xiàn)ShopRepository并注入對(duì)應(yīng)的Model即Shop。別的PostRepository、TagRepository也會(huì)是這么寫(xiě)(當(dāng)然,對(duì)于很多重用的Repository方法可以集體拿到AbstractRepository抽象類里供子類繼承,實(shí)現(xiàn)代碼復(fù)用
)。那能不能直接命令行生成模板文件呢,就不用自己一個(gè)個(gè)的寫(xiě)了,就像輸入php artisan make:controller PostController
給我一個(gè)Controller模板來(lái)。
關(guān)于使用Repository模式來(lái)封裝下Model邏輯,不讓Controller里塞滿了很多Model邏輯,這樣做是有很多好處的,最主要的就是好測(cè)試和代碼架構(gòu)清晰,也符合SOLID原則。如果使用PHPUnit來(lái)做測(cè)試就知道了為啥說(shuō)好測(cè)試了。SegmentFault上也有相關(guān)的文章描述。作者也打算最近新開(kāi)一篇文章聊一聊這個(gè),PHPUnit也打算過(guò)段時(shí)間聊一聊。
個(gè)人研究了下Artisan命令行,是可以的。經(jīng)過(guò)開(kāi)發(fā)后,結(jié)果是輸入自定義指令php artisan make:repository PostRepository --model=Post(這個(gè)option可要可不要)
,就會(huì)幫我生成一個(gè)PostRepositoryInterface和對(duì)應(yīng)的接口實(shí)現(xiàn)PostRepository。
模板文件Stub
由于個(gè)人需要生成一個(gè)RepositoryInterface和對(duì)應(yīng)接口實(shí)現(xiàn)Repository,那就需要兩個(gè)模板文件了。在resources/stubs新建兩個(gè)模板文件,以下是個(gè)人經(jīng)常需要的兩個(gè)模板文件(你可以自定義):
/**
* @param array $columns
* @return \Illuminate\Database\Eloquent\Collection|static[]
*/
public
function
all(
$columns
=
array
(
'*'
))
{
return
$this
->
$model_var_name
->all(
$columns
);
}
/**
* @param int $perPage
* @param array $columns
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
public
function
paginate(
$perPage
= 15,
$columns
=
array
(
'*'
))
{
return
$this
->
$model_var_name
->paginate(
$perPage
,
$columns
);
}
/**
* Create a new $model_var_name
* @param array $data
* @return \$model_namespace
*/
public
function
create(
array
$data
)
{
return
$this
->
$model_var_name
->create(
$data
);
}
/**
* Update a $model_var_name
* @param array $data
* @param $id
* @return \$model_namespace
*/
public
function
update(
$data
= [],
$id
)
{
return
$this
->
$model_var_name
->whereId(
$id
)->update(
$data
);
}
/**
* Store a $model_var_name
* @param array $data
* @return \$model_namespace
*/
public
function
store(
$data
= [])
{
$this
->
$model_var_name
->id =
$data
[
'id'
];
//...
$this
->
$model_var_name
->save();
}
/**
* Delete a $model_var_name
* @param array $data
* @param $id
* @return \$model_namespace
*/
public
function
delete
(
$data
= [],
$id
)
{
$this
->
$model_var_name
->whereId(
$id
)->
delete
();
}
/**
* @param $id
* @param array $columns
* @return array|\Illuminate\Database\Eloquent\Collection|static[]
*/
public
function
find(
$id
,
$columns
=
array
(
'*'
))
{
$
$model_name
=
$this
->
$model_var_name
->whereId(
$id
)->get(
$columns
);
return
$
$model_name
;
}
/**
* @param $field
* @param $value
* @param array $columns
* @return \Illuminate\Database\Eloquent\Collection|static[]
*/
public
function
findBy(
$field
,
$value
,
$columns
=
array
(
'*'
))
{
$
$model_name
=
$this
->
$model_var_name
->where(
$field
,
'='
,
$value
)->get(
$columns
);
return
$
$model_name
;
}
}
模板文件里包括參數(shù),這些參數(shù)將會(huì)根據(jù)命令行中輸入的參數(shù)和選項(xiàng)被相應(yīng)替換:
復(fù)制代碼代碼如下:
- ['$repository_namespace', '$model_namespace', '$repository_interface_namespace', '$repository_interface', '$class_name', '$model_name', '$model_var_name']
Artisan命令生成Repository模板文件
生成Artisan命令并注冊(cè)
Laravel提供了Artisan命令自定義,輸入指令:
php artisan make:console MakeRepositoryCommand
然后改下簽名和描述:
// app/Console/Commands/MakeRepositoryCommand
/**
* The name and signature of the console command.
*
* @var string
*/
protected
$signature
=
'make:repository {repository} {--model=}'
;
/**
* The console command description.
*
* @var string
*/
protected
$description
=
'Make a repository and interface'
;
這里{repository}是必填參數(shù)并指明(選填參數(shù)加個(gè)?
,就和路由參數(shù)一樣),將會(huì)被$this->argument('repository')方法捕捉到,{--model=}是選項(xiàng),可填可不填,將會(huì)被$this->option('model')方法捕捉到。填上這個(gè)命令的描述,最后在Console的Kernel里注冊(cè)下命令:
// app/Console/Kernel
protected
$commands
= [
// Commands\Inspire::class,
// Commands\RedisSubscribe::class,
// Commands\RedisPublish::class,
// Commands\MakeTestRepositoryCommand::class,
Commands\MakeRepositoryCommand::
class
,
];
然后輸入php artisan命令后就能看到這個(gè)make:repository命令了。
自動(dòng)化生成RepositoryInterface和Repository文件
在MakeRepositoryCommand.php命令執(zhí)行文件里寫(xiě)上模板自動(dòng)生成邏輯,代碼也不長(zhǎng),有些邏輯也有注釋,可看:
use
Config;
use
Illuminate\Console\Command;
use
Illuminate\Filesystem\Filesystem;
use
Illuminate\Support\Composer;
class
MakeRepositoryCommand
extends
Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected
$signature
=
'make:repository {repository} {--model=}'
;
/**
* The console command description.
*
* @var string
*/
protected
$description
=
'Make a repository and interface'
;
/**
* @var
*/
protected
$repository
;
/**
* @var
*/
protected
$model
;
/**
* Create a new command instance.
*
* @param Filesystem $filesystem
* @param Composer $composer
*/
public
function
__construct(Filesystem
$filesystem
, Composer
$composer
)
{
parent::__construct();
$this
->files =
$filesystem
;
$this
->composer =
$composer
;
}
/**
* Execute the console command.
*
* @return mixed
*/
public
function
handle()
{
//獲取repository和model兩個(gè)參數(shù)值
$argument
=
$this
->argument(
'repository'
);
$option
=
$this
->option(
'model'
);
//自動(dòng)生成RepositoryInterface和Repository文件
$this
->writeRepositoryAndInterface(
$argument
,
$option
);
//重新生成autoload.php文件
$this
->composer->dumpAutoloads();
}
private
function
writeRepositoryAndInterface(
$repository
,
$model
)
{
if
(
$this
->createRepository(
$repository
,
$model
)){
//若生成成功,則輸出信息
$this
->info(
'Success to make a '
.ucfirst(
$repository
).
' Repository and a '
.ucfirst(
$repository
).
'Interface Interface'
);
}
}
private
function
createRepository(
$repository
,
$model
)
{
// getter/setter 賦予成員變量值
$this
->setRepository(
$repository
);
$this
->setModel(
$model
);
// 創(chuàng)建文件存放路徑, RepositoryInterface放在app/Repositories,Repository個(gè)人一般放在app/Repositories/Eloquent里
$this
->createDirectory();
// 生成兩個(gè)文件
return
$this
->createClass();
}
private
function
createDirectory()
{
$directory
=
$this
->getDirectory();
//檢查路徑是否存在,不存在創(chuàng)建一個(gè),并賦予775權(quán)限
if
(!
$this
->files->isDirectory(
$directory
)){
return
$this
->files->makeDirectory(
$directory
, 0755, true);
}
}
private
function
getDirectory()
{
return
Config::get(
'repository.directory_eloquent_path'
);
}
private
function
createClass()
{
//渲染模板文件,替換模板文件中變量值
$templates
=
$this
->templateStub();
$class
= null;
foreach
(
$templates
as
$key
=>
$template
) {
//根據(jù)不同路徑,渲染對(duì)應(yīng)的模板文件
$class
=
$this
->files->put(
$this
->getPath(
$key
),
$template
);
}
return
$class
;
}
private
function
getPath(
$class
)
{
// 兩個(gè)模板文件,對(duì)應(yīng)的兩個(gè)路徑
$path
= null;
switch
(
$class
){
case
'Eloquent'
:
$path
=
$this
->getDirectory().DIRECTORY_SEPARATOR.
$this
->getRepositoryName().
'.php'
;
break
;
case
'Interface'
:
$path
=
$this
->getInterfaceDirectory().DIRECTORY_SEPARATOR.
$this
->getInterfaceName().
'.php'
;
break
;
}
return
$path
;
}
private
function
getInterfaceDirectory()
{
return
Config::get(
'repository.directory_path'
);
}
private
function
getRepositoryName()
{
// 根據(jù)輸入的repository變量參數(shù),是否需要加上'Repository'
$repositoryName
=
$this
->getRepository();
if
((
strlen
(
$repositoryName
) <
strlen
(
'Repository'
)) ||
strrpos
(
$repositoryName
,
'Repository'
, -11)){
$repositoryName
.=
'Repository'
;
}
return
$repositoryName
;
}
private
function
getInterfaceName()
{
return
$this
->getRepositoryName().
'Interface'
;
}
/**
* @return mixed
*/
public
function
getRepository()
{
return
$this
->repository;
}
/**
* @param mixed $repository
*/
public
function
setRepository(
$repository
)
{
$this
->repository =
$repository
;
}
/**
* @return mixed
*/
public
function
getModel()
{
return
$this
->model;
}
/**
* @param mixed $model
*/
public
function
setModel(
$model
)
{
$this
->model =
$model
;
}
private
function
templateStub()
{
// 獲取兩個(gè)模板文件
$stubs
=
$this
->getStub();
// 獲取需要替換的模板文件中變量
$templateData
=
$this
->getTemplateData();
$renderStubs
= [];
foreach
(
$stubs
as
$key
=>
$stub
) {
// 進(jìn)行模板渲染
$renderStubs
[
$key
] =
$this
->getRenderStub(
$templateData
,
$stub
);
}
return
$renderStubs
;
}
private
function
getStub()
{
$stubs
= [
'Eloquent'
=>
$this
->files->get(resource_path(
'stubs/Repository'
).DIRECTORY_SEPARATOR.
'Eloquent'
.DIRECTORY_SEPARATOR.
'repository.stub'
),
'Interface'
=>
$this
->files->get(resource_path(
'stubs/Repository'
).DIRECTORY_SEPARATOR.
'repository_interface.stub'
),
];
return
$stubs
;
}
private
function
getTemplateData()
{
$repositoryNamespace
= Config::get(
'repository.repository_namespace'
);
$modelNamespace
=
'App\\'
.
$this
->getModelName();
$repositoryInterfaceNamespace
= Config::get(
'repository.repository_interface_namespace'
);
$repositoryInterface
=
$this
->getInterfaceName();
$className
=
$this
->getRepositoryName();
$modelName
=
$this
->getModelName();
$templateVar
= [
'repository_namespace'
=>
$repositoryNamespace
,
'model_namespace'
=>
$modelNamespace
,
'repository_interface_namespace'
=>
$repositoryInterfaceNamespace
,
'repository_interface'
=>
$repositoryInterface
,
'class_name'
=>
$className
,
'model_name'
=>
$modelName
,
'model_var_name'
=>
strtolower
(
$modelName
),
];
return
$templateVar
;
}
private
function
getRenderStub(
$templateData
,
$stub
)
{
foreach
(
$templateData
as
$search
=>
$replace
) {
$stub
=
str_replace
(
'$'
.
$search
,
$replace
,
$stub
);
}
return
$stub
;
}
private
function
getModelName()
{
$modelName
=
$this
->getModel();
if
(isset(
$modelName
) && !
empty
(
$modelName
)){
$modelName
= ucfirst(
$modelName
);
}
else
{
// 若option選項(xiàng)沒(méi)寫(xiě),則根據(jù)repository來(lái)生成Model Name
$modelName
=
$this
->getModelFromRepository();
}
return
$modelName
;
}
private
function
getModelFromRepository()
{
$repository
=
strtolower
(
$this
->getRepository());
$repository
=
str_replace
(
'repository'
,
''
,
$repository
);
return
ucfirst(
$repository
);
}
}
這里把一些常量值放在config/repository.php配置文件里了:
<?php
/**
* Created by PhpStorm.
* User: liuxiang
* Date: 16/6/22
* Time: 17:06
*/
return
[
'directory_path'
=>
'App'
.DIRECTORY_SEPARATOR.
'Repositories'
,
'directory_eloquent_path'
=>
'App'
.DIRECTORY_SEPARATOR.
'Repositories'
.DIRECTORY_SEPARATOR.
'Eloquent'
,
'repository_namespace'
=>
'App\Repositories\Eloquent'
,
'repository_interface_namespace'
=>
'App\Repositories'
,
];
運(yùn)行一下看可不可以吧,這里截個(gè)圖:
It is working!!!
是可以生成RepositoryInterface和對(duì)應(yīng)的接口實(shí)現(xiàn)文件,這里一個(gè)是加了--model選項(xiàng)一個(gè)沒(méi)加的,沒(méi)加的話這里第一個(gè)指令就默認(rèn)Model的名稱是Shop。
生成的文件內(nèi)容不截圖了,看下新生成的ShopRepository.php文件,的確是我想要的模板文件:
總結(jié):本文主要用Laravel的Artisan命令來(lái)自動(dòng)生成個(gè)人需要的模板,減少平時(shí)開(kāi)發(fā)中重復(fù)勞動(dòng)。就像Laravel自帶了很多模板生成命令,用起來(lái)會(huì)節(jié)省很多時(shí)間。這是作者在平時(shí)開(kāi)發(fā)中遇到的問(wèn)題,通過(guò)利用Laravel Artisan命令解決了,所以Laravel還是挺好玩的。有興趣的可以把代碼扒下來(lái)玩一玩,并根據(jù)你自己想要的模板做修改。這兩天想就Repository模式封裝Model邏輯的方法和好處聊一聊,到時(shí)見(jiàn)。希望對(duì)大家的學(xué)習(xí)有所幫助
<?php
/**
* Created by PhpStorm.
* User: liuxiang
*/
namespace
App\Repositories\Eloquent;
use
App\Shop;
use
App\Repositories\ShopRepositoryInterface;
class
ShopRepository
implements
ShopRepositoryInterface
{
/**
* @var \App\Shop
*/
public
$shop
;
public
function
__construct(Shop
$shop
)
{
$this
->shop =
$shop
;
}
/**
* @param array $columns
* @return \Illuminate\Database\Eloquent\Collection|static[]
*/
public
function
all(
$columns
=
array
(
'*'
))
{
return
$this
->shop->all(
$columns
);
}
/**
* @param int $perPage
* @param array $columns
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
public
function
paginate(
$perPage
= 15,
$columns
=
array
(
'*'
))
{
return
$this
->shop->paginate(
$perPage
,
$columns
);
}
/**
* Create a new shop
* @param array $data
* @return \App\Shop
*/
public
function
create(
array
$data
)
{
return
$this
->shop->create(
$data
);
}
/**
* Update a shop
* @param array $data
* @param $id
* @return \App\Shop
*/
public
function
update(
$data
= [],
$id
)
{
return
$this
->shop->whereId(
$id
)->update(
$data
);
}
/**
* Store a shop
* @param array $data
* @return \App\Shop
*/
public
function
store(
$data
= [])
{
$this
->shop->id =
$data
[
'id'
];
//...
$this
->shop->save();
}
/**
* Delete a shop
* @param array $data
* @param $id
* @return \App\Shop
*/
public
function
delete
(
$data
= [],
$id
)
{
$this
->shop->whereId(
$id
)->
delete
();
}
/**
* @param $id
* @param array $columns
* @return array|\Illuminate\Database\Eloquent\Collection|static[]
*/
public
function
find(
$id
,
$columns
=
array
(
'*'
))
{
$Shop
=
$this
->shop->whereId(
$id
)->get(
$columns
);
return
$Shop
;
}
/**
* @param $field
* @param $value
* @param array $columns
* @return \Illuminate\Database\Eloquent\Collection|static[]
*/
public
function
findBy(
$field
,
$value
,
$columns
=
array
(
'*'
))
{
$Shop
=
$this
->shop->where(
$field
,
'='
,
$value
)->get(
$columns
);
return
$Shop
;
}
}
- PHP判斷電子郵件是否正確的簡(jiǎn)單方法介紹
- 當(dāng)laravel獲取不到session的三種解決辦法
- 在PHP中進(jìn)行curl開(kāi)啟操作的具體教程
- PHP中間件ICE,ICE的安裝配置,ICE常見(jiàn)編譯和運(yùn)行(異常)
- win7下手動(dòng)配置apache+php+mysql記
- OneinStack一鍵PHP/JAVA/HHVM安裝及VPS手動(dòng)安裝LNMP
- PHP遭棄用!Wordpress.com開(kāi)源并轉(zhuǎn)用Javascript
- 分享6款國(guó)內(nèi)、外開(kāi)源PHP輕論壇CMS程序
- 【php爬蟲(chóng)】百萬(wàn)級(jí)別知乎用戶數(shù)據(jù)爬取與分析
- FreeHostia免費(fèi)PHP空間中文面板250MB空間6GB流量
PHP判斷電子郵件是否正確的簡(jiǎn)單方法介紹
本篇內(nèi)容里小編給大家整理了一篇關(guān)于php判斷電子郵件是否正確方法,需要的朋友們參考下。PHP判斷電子郵件是否正確即是否有效,是我們PHP面試過(guò)程中常見(jiàn)考題之一。我們可以使用P...
當(dāng)laravel獲取不到session的三種解決辦法
說(shuō)到PHP估計(jì)有些朋友還有些熟悉,但是對(duì)于PHP下的laravel就表示不知道了。有些人因?yàn)樾枰褂玫絣aravel時(shí),卻發(fā)現(xiàn)過(guò)程中出現(xiàn)了問(wèn)題,那就是session無(wú)法獲取了,鼓搗了半天也不...
在PHP中進(jìn)行curl開(kāi)啟操作的具體教程
相信大家對(duì)于PHP都并不陌生了,畢竟PHP吸收了C語(yǔ)言、Java里的許多優(yōu)點(diǎn),作為一款腳本語(yǔ)言還是收到很多程序員朋友的喜愛(ài)的。PHP里的curl函數(shù)庫(kù)還是非常實(shí)用的,今天小編就給大...
PHP中間件ICE,ICE的安裝配置,ICE常見(jiàn)編譯和運(yùn)行(異常)錯(cuò)誤
ICE(Internet Communications Engine)是Zeroc提供的一款高性能的中間件。使用ICE能使得php(或c++,java,python)與java,c++,.net,python等進(jìn)行交互?;贗CE可以實(shí)現(xiàn)電信級(jí)的...
win7下手動(dòng)配置apache+php+mysql記
本來(lái)想學(xué)學(xué)php,于是就想搭建web服務(wù)器和sql環(huán)境,結(jié)果浪費(fèi)掉了不少時(shí)間。大致的總結(jié)下,也算是長(zhǎng)個(gè)記性。使用的安裝包分別是httpd-2.2.22-win32-x86-no_ssl.msi,php-5.2.5-Win32.zip和mysql-installer-communit...
OneinStack一鍵PHP/JAVA/HHVM安裝及VPS手動(dòng)安裝LNMP
說(shuō)起LNMP,多數(shù)人應(yīng)該知道lnmp.org站長(zhǎng)開(kāi)發(fā)的LNMP一鍵安裝包,該腳本虛擬主機(jī)管理、FTP用戶管理、Nginx、MySQL/MariaDB、PHP的升級(jí)、常用緩存組件的安裝、重置MySQLroot密...
PHP遭棄用!Wordpress.com開(kāi)源并轉(zhuǎn)用Javascript
據(jù)外媒消息稱,Wordpress母公司Automattic將完全重寫(xiě)Wordpress.com網(wǎng)站代碼,并將此項(xiàng)計(jì)劃命名為“Calypso”,代碼開(kāi)源并被托管于Github平臺(tái)。此外,最新的wordpress.com放...
分享6款國(guó)內(nèi)、外開(kāi)源PHP輕論壇CMS程序
隨著移動(dòng)互聯(lián)網(wǎng)對(duì)于傳統(tǒng)互聯(lián)網(wǎng)的沖擊,用戶群更加注重信息的及時(shí)性和有效性的簡(jiǎn)便分享和獲取,傳統(tǒng)的社區(qū)模式經(jīng)過(guò)多年的積累沉淀很深,尤其對(duì)于新興的社區(qū)用戶群和站長(zhǎng)來(lái)說(shuō)...
【php爬蟲(chóng)】百萬(wàn)級(jí)別知乎用戶數(shù)據(jù)爬取與分析
本程序是抓取知乎的用戶數(shù)據(jù),要能訪問(wèn)用戶個(gè)人頁(yè)面,需要用戶登錄后的才能訪問(wèn)。當(dāng)我們?cè)跒g覽器的頁(yè)面中點(diǎn)擊一個(gè)用戶頭像鏈接進(jìn)入用戶個(gè)人中心頁(yè)面的時(shí)候,之所以能夠看到...
FreeHostia免費(fèi)PHP空間中文面板250MB空間6GB流量
FreeHostia這類空間提供中文主機(jī)控制面板,有域名綁定、MysqL管理、在線文件管理器、PHP配置調(diào)整等,特別適合新手朋友們體驗(yàn)建站,搭建一個(gè)屬于個(gè)人的博客。...