Laravel 中通过 Artisan View 扩展包创建及删除应用视图文件
1、简介
本扩展包添加了两个视图相关的Artisan命令到Laravel应用,以便我们通过Artisan命令即可创建和管理视图文件,可谓是进一步解放了生产力。
2、安装
还是通过Composer安装:
composer require sven/artisan-view
安装完成后到config/app.php中注册服务提供者ArtisanViewServiceProvider到providers数组:
// config/app.php
'providers' => [
...
Sven\ArtisanView\ArtisanViewServiceProvider::class,
];
3、使用
如果你现在运行 php artisan 会发现多出如下两个命令,分别用于创建和删除视图文件:
- make:view
- scrap:view
创建视图
# 在视图根目录'views'下创建'index.blade.php'视图文件
$ php artisan make:view index
# 在子目录'pages’下创建'index.blade.php'视图文件
$ php artisan make:view pages.index
# 在自定义目录(相对于系统根目录)下创建视图文件
$ php artisan make:view index --directory=custom/path
# 指定视图文件扩展名
$ php artisan make:view index --extension=html
# 继承一个已存在的视图
$ php artisan make:view index --extends=app
# 在视图中添加title和content部分
$ php artisan make:view index --sections=title,content
# 创建一个名为products的资源(包含index、create、edit、show视图文件)
$ php artisan make:view products --resource
# 创建指定动作资源(index、create和edit)
$ php artisan make:view products --resource --verbs=index,create,edit
# 创建继承自layout且包含foo、bar的资源文件
$ php artisan make:view products --resource --extends=layout --sections=foo,bar
删除视图
# 删除视图文件 'index.blade.php'
$ php artisan scrap:view index
# 通过.删除子目录视图文件
$ php artisan scrap:view pages.index
为什么使用Trait
PHP语言使用一种典型的单继承模型,在这种模型中,我们先编写一个通用的根类,实现基本的功能,然后扩展这个根类,创建更具体的子类,直接从父类继承实现。这叫做继承层次结构,很多编程语言都使用这个模式。大多数时候这种典型的继承模型能够良好运作,但是如果想让两个无关的PHP类具有类似的行为,应该怎么做呢?
Trait就是为了解决这种问题而诞生的。Trait能够把模块化的实现方式注入多个无关的类中,从而提高代码复用,符合DRY(Don’t Repeat Yourself)原则。比如Laravel底层用户认证相关逻辑以及软删除实现等地方都使用了Trait来实现。以Laravel自带的AuthController
为例,其中的登录、注册以及登录失败尝试次数都是通过Trait实现:
如何创建Trait
创建Trait很简单,跟创建类有点类似,只不过使用的关键字是trait
而不是class
,以上述ThrottlesLogin
为例:
trait
声明定义的是一个Trait,然后我们可以在这个Trait中像类一样定义要使用的属性和方法。此外Trait支持嵌套和组合,即通过一个或多个Trait(多个用,分隔)组合成一个Trait,比如AuthenticatesAndRegistersUsers
即是如此:
insteadof
关键字,如果AuthenticatesUsers
和RegistersUsers
中都定义了redirectPath
和getGuard
方法,那么将从AuthenticatesUsers
中获取对应方法而不是RegistersUsers
。另外还可以使用as
关键字为方法起个别名,这样也可以避免命名冲突。如何使用Trait
Trait的使用方法也很简单,上面已经显示的很清楚明了,即使用use
关键字。
use
关键字,不同之处在于导入位置,命名空间在类的定义体外导入,而Trait在类的定义体内导入。代码如下 | 复制代码 |
注:PHP解释器在编译时会把Trait复制到类的定义体中,但是不会处理这个操作引入的不兼容问题,如果Trait假定类中有特定的属性或方法,需要先确保类中确实有相应的属性或方法。
|
1、概述
闭包和匿名函数在PHP 5.3.0中引入,这两个特性非常有用,每个PHP开发者都应该掌握。
闭包是指在创建时封装周围状态的函数,即使闭包所在的环境的不存在了,闭包中封装的状态依然存在。
匿名函数其实就是没有名称的函数,匿名函数可以赋值给变量,还能像其他任何PHP函数对象那样传递。不过匿名函数仍然是函数,因此可以调用,还可以传入参数,适合作为函数或方法的回调。
注:理论上讲闭包和匿名函数是不同的概念,不过PHP将其视作相同的概念(匿名函数在PHP中也叫作闭包函数),所以下面提到闭包时指的也是匿名函数;反之亦然。
2、创建闭包
创建闭包很简单:
<?php
$greet = function ($name) {
return sprintf("Hello %s\r\n", $name);
};
echo $greet('111cn.net');
结果打印:
Hello 111cn.net
闭包和普通的PHP函数很像:常用的句法相同,也接受参数,而且能返回值。不过闭包没有函数名。
注:我们之所以能调用$greet变量,是因为这个变量的值是一个闭包,而且闭包对象实现了__invoke()魔术方法,只要变量名后有(),PHP就会查找并调用__invoke方法。
我们通常把PHP闭包当做函数会方法的回调使用,事实上,很多PHP函数都会用到闭包,比如array_map和preg_replace_callback,这是使用PHP匿名函数的绝佳时机。记住,闭包和其他值一样,可以作为参数传入其他PHP函数:
<?php
$numberPlusOne = array_map(function ($number) {
return $number += 1;
}, [1, 2, 3]);
print_r($numberPlusOne);
在闭包出现之前,要实现这样的功能,PHP开发者只能单独创建具名函数,然后使用名称引用这个函数:
<?php
function incrementNumner ($number) {
return $number += 1;
}
$numberPlusOne = array_map(‘incrementNumber’, [1, 2, 3]);
print_r($numberPlusOne);
这样做把回调的实现和使用场所隔离开了,而且使用闭包实现代码更加简洁。
3、从父作用域继承变量
在PHP中必须手动调用闭包对象的bindTo方法或使用use关键字把父作用域的变量及状态附加到PHP闭包中。而实际应用中,又以使用use关键字实现居多。
use关键字
实际上,Laravel框架中也大量使用了闭包,最常见的比如路由定义:
Route::group(['domain' => '{account}.myapp.com'], function () {
Route::get('user/{id}', function ($account, $id) {
//
});
});
这里面的两个function都是闭包。而从父作用域继承变量的使用场景在Laravel底层源码中也是俯拾即是,比如Model.php(Illuminate\Database\Eloquent)的saveOrFail方法:
closure-use
该方法的作用是使用事务将模型数据保存到数据库,这里面我们使用闭包返回保存状态,同时使用use关键字将父作用域的$options传递给该闭包以便其能够访问这个数据。
此外,还支持传递多个父作用域变量到闭包,比如还是在Model类中的forceFill方法:
closure-use-multi
多个变量以逗号分隔即可。
bindTo方法
我们在前面已经提到,闭包是一个对象,所以我们可以在闭包中使用$this关键字获取闭包的内部状态,闭包对象的默认状态没什么用,需要注意的是其中的__invoke魔术方法和bindTo方法。
__invoke的作用前面已经说过,当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。
接下来我们来看看bindTo方法,通过该方法,我们可以把闭包的内部状态绑定到其他对象上。这里bindTo方法的第二个参数显得尤为重要,其作用是指定绑定闭包的那个对象所属的PHP类,这样,闭包就可以在其他地方访问邦定闭包的对象中受保护和私有的成员变量。
你会发现,PHP框架经常使用bindTo方法把路由URL映射到匿名回调函数上,框架会把匿名回调函数绑定到应用对象上,这样在匿名函数中就可以使用$this关键字引用重要的应用对象:
<?php
class App {
protected $routes = [];
protected $responseStatus = '200 OK';
protected $responseContentType = 'text/html';
protected $responseBody = 'Laravel学院';
public function addRoute($routePath, $routeCallback) {
$this->routes[$routePath] = $routeCallback->bindTo($this, __CLASS__);
}
public function dispatch($currentPath) {
foreach ($this->routes as $routePath => $callback) {
if( $routePath === $currentPath) {
$callback();
}
}
header('HTTP/1.1 ' . $this->responseStatus);
header('Content-Type: ' . $this->responseContentType);
header('Content-Length: ' . mb_strlen($this->responseBody));
echo $this->responseBody;
}
}
这里我们需要重点关注addRoute方法,这个方法的参数分别是一个路由路径和一个路由回调,dispatch方法的参数是当前HTTP请求的路径,它会调用匹配的路由回调。第9行是重点所在,我们将路由回调绑定到了当前的App实例上。这么做能够在回调函数中处理App实例的状态:
$app = new App();
$app->addRoute(‘user/nonfu’, function(){
$this->responseContentType = ‘application/json;charset=utf8’;
$this->responseBody = ‘{“name”:”LaravelAcademy"}';
});
$app->dispatch(‘user/nonfu');
在Larval底层也有用到bindTo方法,详见Illuminate\Support\Traits\Macroable的__call方法:
1、概述
生成器是 PHP 5.5 引入的新特性,但是目测很少人用到它,其实这是个非常有用的功能。
生成器和迭代器有点类似,但是与标准的PHP迭代器不同,PHP生成器不要求类实现Iterator接口,从而减轻了类的开销和负担。生成器会根据需求每次计算并产出需要迭代的值,这对应用的性能有很大的影响:试想假如标准的PHP迭代器经常在内存中执行迭代操作,这要预先计算出数据集,性能低下;如果要使用特定方式计算大量数据,如操作Excel表数据,对性能影响更甚。此时我们可以使用生成器,即时计算并产出后续值,不占用宝贵的内存空间。
2、创建生成器
生成器的创建方式很简单,因为生成器就是PHP函数,只不过要在函数中一次或多次使用yield关键字。与普通的PHP函数不同的是,生成器从不返回值,只产出值。下面是一个简单的生成器实现:
function getLaravelAcademy() {
yield www.111cn.net';
yield 'Laravel学院';
yield 'Laravel Academy';
}
很简单吧!调用此生成器函数时,PHP会返回一个属于Generator类的对象,这个对象可以使用foreach函数迭代,每次迭代,PHP会要求Generator实例计算并提供下一个要迭代的值。生成器的优雅体现在每次产出一个值之后,生成器的内部状态都会停顿;向生成器请求下一个值时,内部状态又会恢复。生成器内部的状态会一直在停顿和恢复之间切换,直到抵达函数定义体的末尾或遇到空的return语句为止。我们可以使用下面的代码调用并迭代上面定义的生成器:
foreach(getLaravelAcademy() as $yieldedValue) {
echo $yieldedValue, PHP_EOL;
}
上面代码输出如下:
Laravel Academy
3、使用生成器
下面我们实现一个简单的函数用于生成一个范围内的数值,以此说明生成器是如何节省内存的。首先我们通过迭代器来实现:
function makeRange($length) {
$dataSet = [];
for ($i=0; $i<$length; $i++) {
$dataSet[] = $i;
}
return $dataSet;
}
$customRange = makeRange(1000000);
foreach ($customRange as $i) {
echo $i . PHP_EOL;
}
此时执行会报错,提示超出单个PHP进程内存限制(要为100万个数字提供内存空间):
memery-overflow-iterator
下面我们来改进实现方案,使用生成器实现如下:
function makeRange($length) {
for ($i=0; $i<$length; $i++) {
yield $i;
}
}
foreach (makeRange(1000000) as $i) {
echo $i . PHP_EOL;
}
再次执行就可以毫无压力的打印出结果,因为生成器每次只需要为一个整数分配内存。
此外,一个常用的使用案例就是使用生成器迭代流资源(文件、音频等)。假设我们想要迭代一个大小为4GB的CSV文件,而虚拟私有服务器(VPS)只允许PHP使用1GB内存,因此不能把整个文件都加载到内存中,下面的代码展示了如何使用生成器完成这种操作:
function getRows($file) {
$handle = fopen($file, 'rb');
if ($handle == FALSE) {
throw new Exception();
}
while (feof($handle) === FALSE) {
yield fgetcsv($handle);
}
fclose($handle);
}
foreach ($getRows($file) as $row) {
print_r($row);
}
上述示例一次只会为CSV文件中的一行分配内存,而不会把整个4GB的CSV文件都读取到内存中。
4、总结
生成器是功能多样性和简洁性之间的折中方案,生成器只是向前进的迭代器,这意味着不能使用生成器在数据集中执行后退、快进或查找操作,只能让生成器计算并产出下一个值。迭代大型数据集或数列时最适合使用生成器,因为这样占用的系统内存最少。生成器也能完成迭代器能完成的简单任务,而且使用的代码更少。
本文章来为各位介绍一篇关于Laravel memcached缓存对文章增删改查进行优化例子,希望这篇文章能够帮助到各位。本节我们将以文章的增删改查作为实例系统讲述缓存的使用,这个实例是对之前创建RESTFul风格控制器实现文章增删改查这篇教程的改造和升级,我们将在其基础上融合进Eloquent ORM和模型事件,将应用的场景直接拉到生成环境。
1、准备工作
路由及控制器
路由的定义和控制器的创建保持和创建RESTFul风格控制器实现文章增删改查中一样。
创建数据表
关于文章对应数据表我们在数据库部分使用查询构建器实现对数据库的高级查询已有提及,这里我们使用之前创建的数据表即可。
创建文章模型
关于文章模型Post的创建也和之前Eloquent ORM部分讲ORM概述、模型定义及基本查询中创建的一致。
2、修改控制器
在之前我们是通过缓存实现对文章的增删改查操作,这里我们将其修改为通过数据库实现增删改查操作:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Cache;
use App\Models\Post;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
/**
* 显示文章列表.
*
* @return Response
*/
public function index()
{
//使用all获取所有数据,如果数据量大采用分页获取
$posts = Post::all();
if(!$posts)
exit('还没有发布任何文章!');
$html = '<ul>';
foreach ($posts as $post) {
$html .= '<li><a href='.route('post.show',['post'=>$post]).'>'.$post->title.'</li>';
}
$html .= '</ul>';
return $html;
}
/**
* 创建新文章表单页面
*
* @return Response
*/
public function create()
{
$postUrl = route('post.store');
$csrf_field = csrf_field();
$html = <<<CREATE
<form action="$postUrl" method="POST">
$csrf_field
<input type="text" name="title"><br/><br/>
<textarea name="content" cols="50" rows="5"></textarea><br/><br/>
<input type="submit" value="提交"/>
</form>
CREATE;
return $html;
}
/**
* 将新创建的文章存储到存储器
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$title = $request->input('title');
$content = $request->input('content');
$post = new Post;
$post->title = $title;
$post->content = $content;
$post->save();
return redirect()->route('post.show',['post'=>$post]);
}
/**
* 显示指定文章
*
* @param int $id
* @return Response
*/
public function show($id)
{
$post = Cache::get('post_'.$id);
if(!$post){
$post = Post::find($id);
if(!$post)
exit('指定文章不存在!');
Cache::put('post_'.$id,$post,60*24*7);
}
if(!Cache::get('post_views_'.$id))
Cache::forever('post_views_'.$id,0);
$views = Cache::increment('post_views_'.$id);
Cache::forever('post_views_'.$id,$views);
$editUrl = route('post.edit',['post'=>$post]);
$deleteUrl = route('post.destroy',['post'=>$post]);
$html = <<<POST
<h3>{$post->title}</h3>
<p>{$post->content}</p>
<i>已有{$views}人阅读</i>
<p>
<a href="{$editUrl}">编辑</a>
</p>
POST;
return $html;
}
/**
* 显示编辑指定文章的表单页面
*
* @param int $id
* @return Response
*/
public function edit($id)
{
$post = Post::find($id);
if(!$post)
exit('指定文章不存在!');
$postUrl = route('post.update',['post'=>$post]);
$csrf_field = csrf_field();
$html = <<<CREATE
<form action="$postUrl" method="POST">
$csrf_field
<input type="hidden" name="_method" value="PUT"/>
<input type="text" name="title" value="{$post->title}"><br/><br/>
<textarea name="content" cols="50" rows="5">{$post->content}</textarea><br/><br/>
<input type="submit" value="提交"/>
</form>
CREATE;
return $html;
}
/**
* 在存储器中更新指定文章
*
* @param Request $request
* @param int $id
* @return Response
*/
public function update(Request $request, $id)
{
$post = Post::find($id);
if(!$post)
exit('指定文章不存在!');
$title = $request->input('title');
$content = $request->input('content');
$post->title = $title;
$post->content = $content;
$post->save();
return redirect()->route('post.show',['post'=>$post]);
}
/**
* 从存储器中移除指定文章
*
* @param int $id
* @return Response
*/
public function destroy($id)
{
$post = Post::find($id);
if(!$post)
exit('指定被删除文章不存在!');
if($post->delete()){
redirect()->route('post.index');
}else{
exit('删除文章失败!');
}
}
}
需要注意的是在show方法中,我们首先从缓存中取文章数据,缓存中不存在才会去数据库取,同时将数据回写到缓存中,由于对数据库的操作大部分都是读操作,所以这一点小小的改进对性能却有很大提升,尤其是在海量数据时。此外我们还将访问量持久化到缓存中以提升性能。
3、在模型事件中使用缓存
我们还可以通过模型事件在文章进行增删改的时候触发相应事件将修改保存到缓存中,这里我们简单讲模型事件注册到AppServiceProvider的boot方法中:
//保存之后更新缓存数据
Post::saved(function($post){
$cacheKey = 'post_'.$post->id;
$cacheData = Cache::get($cacheKey);
if(!$cacheData){
Cache::add($cacheKey,$post,60*24*7);
}else{
Cache::put($cacheKey,$post,60*24*7);
}
});
//删除之后清除缓存数据
Post::deleted(function($post){
$cacheKey = 'post_'.$post->id;
$cacheData = Cache::get($cacheKey);
if($cacheData){
Cache::forget($cacheKey);
}
if(Cache::get('post_views_'.$post->id))
Cache::forget('post_views_'.$post->id);
});
我们将缓存有效期设置为一周。这样在文章创建或更新时会将数据保存到缓存,而删除文章时也会从缓存中移除数据,从而保证被删除后的文章查看详情时也不能浏览。
相关文章
- 下面小编来给大家演示几个php操作zip文件的实例,我们可以读取zip包中指定文件与删除zip包中指定文件,下面来给大这介绍一下。 从zip压缩文件中提取文件 代...2016-11-25
Jupyter Notebook读取csv文件出现的问题及解决
这篇文章主要介绍了JupyterNotebook读取csv文件出现的问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2023-01-06- 如果我们需要安培Laravel4的话最php最低要求要在php5.3.7版本并且我们需要把mcrypt与openss这两个扩展开启才可以,具体步骤我们参考下文。 前面我们介绍我了 com...2016-11-25
- 有时我们接受或下载到的PSD文件打开是空白的,那么我们要如何来解决这个 问题了,下面一聚教程小伙伴就为各位介绍Photoshop打开PSD文件空白解决办法。 1、如我们打开...2016-09-14
- 这篇文章主要介绍了解决python 使用openpyxl读写大文件的坑,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-13
- 这篇文章主要介绍了C#实现HTTP下载文件的方法,包括了HTTP通信的创建、本地文件的写入等,非常具有实用价值,需要的朋友可以参考下...2020-06-25
- C#使用System.IO中的文件操作方法在Windows系统中处理本地文件相当顺手,这里我们还总结了在Oracle中保存文件的方法,嗯,接下来就来看看整理的C#操作本地文件及保存文件到数据库的基本方法总结...2020-06-25
- 复制代码 代码如下: <td> <a href="/member/life/edit_ppt/<?php echo $v->id;?>" class="btn">编辑</a> <a href="javascript:;" onclick="if(confirm('您确定删除这条记录?')){location.href='/member/life/d...2014-06-07
- 这篇文章主要为大家详细介绍了SpringBoot实现excel文件生成和下载,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-09
Laravel 调试工具 laravel-debugbar 打印日志消息
laravel-debugbar 调试工具的教程小编整理了几篇不错的教程,今天我们来看一篇Laravel 调试工具 laravel-debugbar 打印日志消息例子,希望文章对各位有帮助。 其实不...2016-11-25php无刷新利用iframe实现页面无刷新上传文件(1/2)
利用form表单的target属性和iframe 一、上传文件的一个php教程方法。 该方法接受一个$file参数,该参数为从客户端获取的$_files变量,返回重新命名后的文件名,如果上传失...2016-11-25Laravel 5.1中定义事件、事件监听器以及触发事件例子
下文我们来看一篇关于Laravel 5.1中定义事件、事件监听器以及触发事件例子,希望能够帮助到各位新手朋友的哦。 这里我们基于之前基于模型+缓存对文章增删改查这篇...2016-11-25- 要替换字符串中的内容我们只要利用php相关函数,如strstr,str_replace,正则表达式了,那么我们要替换目录所有文件的内容就需要先遍历目录再打开文件再利用上面讲的函数替...2016-11-25
- 又码了一个周末的代码,这次在做一些关于文件上传的东西。(PHP UPLOAD)小有收获项目是一个BT种子列表,用户有权限上传自己的种子,然后配合BT TRACK服务器把种子的信息写出来...2016-11-25
- 步骤:Window -> PHP -> Editor -> Templates,这里可以设置(增、删、改、导入等)管理你的模板。新建文件注释、函数注释、代码块等模板的实例新建模板,分别输入Name、Description、Patterna)文件注释Name: 3cfileDescriptio...2013-10-04
- 今天小编在这里就来给photoshop的这一款软件的使用者们来说下AI源文件转photoshop图像变模糊问题的解决教程,各位想知道具体解决方法的使用者们,那么下面就快来跟着小编...2016-09-14
- 这篇文章主要介绍了C++万能库头文件在vs中的安装步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
- 本篇文章主要说明的是与php文件上传的相关配置的知识点。PHP文件上传功能配置主要涉及php.ini配置文件中的upload_tmp_dir、upload_max_filesize、post_max_size等选项,下面一一说明。打开php.ini配置文件找到File Upl...2015-10-21
ant design中upload组件上传大文件,显示进度条进度的实例
这篇文章主要介绍了ant design中upload组件上传大文件,显示进度条进度的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-10-29- 这篇文章主要介绍了C#使用StreamWriter写入文件的方法,涉及C#中StreamWriter类操作文件的相关技巧,需要的朋友可以参考下...2020-06-25