在我的上一篇文章我简单的介绍了一下Nova的用法,下面我将通过新建一个简单的CRM系统来更深入的了解Nova

在此过程中,我们将了解如何使用Nova的 metrics , 自定义搜索条, 配置 filters 和 lenses,听起来很复杂,咱们一步一步开始吧!

本文代码:https://github.com/leienshu/learn-nova

开始

不会安装Nova的请参考我的上一篇写Nova的入门文章, 本文默认你已经安装好了,并且创建了一个登录用户。

访客提交

在我们深入了解Nova之前,我们需要创建Laravel模型。 我们的CRM将创建使用两个模型。

  • 一个Visitor模型,我们将用它来为我们跟踪与我们互动的所有访客。
  • 一个Note模型,我们的管理员用户可以为潜在客户留下笔记,访客也可以留下笔记。

因此,我们的模型关系,我们的潜在客户将拥有许多Notes,我们的管理员用户也将拥有许多Notes。

下面我们开始创建模型:

php artisan make:model Visitor -a
php artisan make:model Note -a

上面-a参数的意思是同时创建 Model、Migration、Controller、Factory 。

迁移

修改visitors的migration文件:

public function up()
    {
        Schema::create('visitors', function (Blueprint $table) {
            $table->increments('id');
            $table->text('type')->comment('类型');
            $table->text('status')->comment('状态');
            $table->text('name')->comment('名字');
            $table->text('email')->comment('邮箱');
            $table->timestamps();
        });
    }

修改notes的migration文件:

public function up()
    {
        Schema::create('notes', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->comment('用户ID');
            $table->integer('visitor_id')->comment('访客ID');
            $table->text('priority')->comment('优先级');
            $table->text('title')->comment('标题');
            $table->text('body')->comment('内容');
            $table->timestamps();
        });
    }

执行迁移:

php artisan migrate

模型

我们的每一条note既有访客又有管理用户,所以我们定义note的模型如下:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Note extends Model
{
    public function user()
    {
        return $this->belongsTo('App\User');
    }

    public function visitor()
    {
        return $this->belongsTo('App\visitor');
    }
}

然后我们继续添加我们的User模型和Visitor模型和Note模型的关系:
都增加以下代码:

public function notes()
    {
        return $this->hasMany('App\Note');
    }

好了,我们继续在Visitor模型中添加一些fillable属性。

protected $fillable = [
        'type',
        'status',
        'email',
        'name'
    ];

最后,让我们在Visitor和Note模型中提供一些常量和辅助方法,以便我们可以轻松访问它们的类型,状态和优先级值。

// Note.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Note extends Model
{
    // 定义优先级
    const LOW_PRIORITY = 'Low';
    const MEDIUM_PRIORITY = 'Medium';
    const HIGH_PRIORITY = 'High';

    public function user()
    {
        return $this->belongsTo('App\User');
    }

    public function visitor()
    {
        return $this->belongsTo('App\Visitor');
    }

    // 获取优先级
    public static function getPriorities()
    {
        return [
            self::LOW_PRIORITY => self::LOW_PRIORITY,
            self::MEDIUM_PRIORITY => self::MEDIUM_PRIORITY,
            self::HIGH_PRIORITY => self::HIGH_PRIORITY,
        ];
    }
}

// Visitor.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Visitor extends Model
{
    // 定义用户常量
    const ORGANIC_TYPE = 'Organic';  //基本类型
    const USER_SUBMITTED_TYPE = 'User Submitted';  //用户提交类型

    const PROSPECT_STATUS = 'Prospect';  //预期
    const VISITOR_STATUS = 'Visitor';  //访客状态
    const CUSTOMER_STATUS = 'Customer'; //客户状态

    protected $fillable = [
        'type',
        'status',
        'email',
        'name'
    ];

    public function notes()
    {
        return $this->hasMany('App\Note');
    }

    // 获取类型
    public static function getTypes()
    {
        return [
            self::ORGANIC_TYPE => self::ORGANIC_TYPE,
            self::USER_SUBMITTED_TYPE => self::USER_SUBMITTED_TYPE,
        ];
    }

    //获取状态
    public static function getStatuses()
    {
        return [
            self::PROSPECT_STATUS => self::PROSPECT_STATUS,
            self::VISITOR_STATUS => self::VISITOR_STATUS,
            self::CUSTOMER_STATUS => self::CUSTOMER_STATUS,
        ];
    }
}

上面定义的常量有点麻烦,如果你不喜欢它,请随意忽略这一部分。我这样做只是为了更好地让我们将使用这些方法来帮助我们使模型与他们的Nova资源保持同步。

就这样,我们完成了我们的模型。

现在,让我们在我们的应用程序里创建一个快速表单,以便访问者可以输入他们的详细信息并成为潜在客户。

访客表单

我们继续用welcome.blade.php这个视图文件,做一些修改:

<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
    <meta charset="utf-8">
    <title>Nova CRM 演示</title>
</head>
<body>
    <div>
        <form action="/form-submit" method="POST">
            <h1>注册我们的Nova程序</h1>
            {{ csrf_field() }}
                <div>
                    <label>姓名:</label>
                    <input id="name" name="name" type="text" placeholder="Lei">
                </div>
                <div>
                    <label>邮箱:</label>
                    <input id="email" name="email" type="email" placeholder="lei@example.com">
                </div>
                <button type="submit">提交</button>
        </form>
        @if (session('form-success'))
            <div>
                <p>{{ session('form-success') }}</p>
            </div>
        @endif
    </div>
</body>
</html>
欢迎页

弄好了这个后就去web.php文件里面加路由吧!

Route::post('/form-submit', 'VisitorController@store');

建好了路由就修改控制器吧:

public function store(Request $request)
    {
        $validatedData = $request->validate([
        'name' => 'required|string',
        'email' => 'required|email|unique:visitors,email',
        ]);

        $visitor = new Visitor;

        $visitor->name = $validatedData['name'];
        $visitor->email = $validatedData['email'];
        $visitor->type = Visitor::ORGANIC_TYPE;
        $visitor->status = Visitor::PROSPECT_STATUS;

        $visitor->save();

        return redirect()->back()
            ->with('form-success', 'Thank you for your submission!');
    }

好了,这样我们的访客就能提交东西了。

Nova CRM

新建resources,使用下面的命令创建nova的resources:

php artisan nova:resource Visitor
php artisan nova:resource Note 

我们看到Nova文件夹下面多处了一个Visitor的recource和一个Note的recource,编辑它们:
修改Visitor的recource:

<?php

namespace App\Nova;

use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Fields\Select;
use Laravel\Nova\Fields\HasMany;
use Illuminate\Http\Request;
use Laravel\Nova\Http\Requests\NovaRequest;

class Visitor extends Resource
{
    public static $model = 'App\Visitor';

    public static $title = 'name';

    public static $search = [
        'id',
        'name',
        'email'
    ];

    public function fields(Request $request)
    {
        return [
            ID::make()->sortable(),

            Text::make('name')->sortable(),

            Select::make('Status')
                ->options(\App\Visitor::getStatuses())
                ->sortable()
                ->rules('required', 'string'),

            Select::make('Type')
                ->options(\App\Visitor::getTypes())
                ->sortable()
                ->rules('required', 'string'),

            Text::make('Email')
                ->sortable()
                ->rules('required', 'email')
                ->creationRules('unique:visitors,email')
                ->updateRules('unique:visitors,email,{{resourceId}}'),

            HasMany::make('Notes'),
        ];
    }
}

正如你所看到的,我这里用了比前一篇文章更多的内容。

在我们的电子邮件字段中,我们正在进行一些特殊验证,以确保我们保持电子邮件的唯一性。

另外,请注意我们的模型方法现在正在出现! 我们可以使用它们来填充选择字段,因此我们的值可以保持同步。也许有点矫枉过正,但我知道我只要在一个地方改变它,所有地方都会同步更新。

最后,我们可以看到索引中出现的所有字段都是可排序的。

打开我们的Nova管理员界面,我们可以看到索引已经自行构建,我们的表单现在可用了!

note
visitor

现在访问者可以提交数据,我们的团队可以上传他们的潜在客户!如果我们能够跟踪管理员用户在Visitors和Notes上为管理目的所做的任何编辑或更改,那将是很好的。 很幸运的是,Nova让这变得非常容易。

我们只需要将 Actionable trait添加到模型中,我们将获得在模型上一些列执行的操作列表。

//In app\Visitor.php 和 app\Note.php
use Actionable;

好了,Visitor可以进行添加操作了,加完后如下图:

visitor

现在,让我们来操作Notes吧,打开:Note Recource

<?php

namespace App\Nova;

use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Fields\Select;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Fields\Markdown;
use Illuminate\Http\Request;
use Laravel\Nova\Http\Requests\NovaRequest;

class Note extends Resource
{

    public static $model = 'App\Note';

    public static $title = 'title';

    public static $search = [
        'id',
        'title',
    ];

    public function fields(Request $request)
    {
        return [
            ID::make()->sortable(),
            Text::make('Title')
                ->sortable()
                ->rules('required', 'string'),
            Select::make('Priority')
                ->options(\App\Note::getPriorities())
                ->sortable()
                ->rules('required', 'string'),
            Markdown::make('Body')
                ->rules('required', 'string'),
            BelongsTo::make('Visitor')
                ->sortable()
                ->rules('required'),
            BelongsTo::make('User')
                    ->sortable()
                ->rules('required'),
        ];
    }
}

继续修改User Recource,让它能关联到Notes:
只要增加两行代码:自己找相应的位置

use Laravel\Nova\Fields\HasMany;
HasMany::make('Notes')

弄好了就可以添加记录了,如下图:


好了我们的CRM的CURD到这里就搞定了,不过你以为完了吗,没有,我们继续增加更多的特性吧!

Metrics

什么是没有任何指标的CRM?开箱即用的Nova使我们可以非常轻松地向管理员添加指标。

那么,我们想看到什么?

随着时间的推移,看到访客的增长会很棒;

我们在一定时期内获得了多少潜在客户,以及每种潜在客户的细分情况。

我们赶紧来设置下吧!

首先让我们用下面的命令来创建Metric吧:

php artisan nova:trend VisitorsPerDay

将caculate方法里默认的模型的值替换一下:

public function calculate(Request $request)
{
    return $this->countByDays($request, \App\Visitor::class);
}

现在,为了显示我们的新趋势图,我们将回到我们的Visitor Recource 并在cards()方法中注册它。由于我们希望它占据屏幕的三分之一,我们还将使用width(’1/3’)方法进行链接。

public function cards(Request $request)
{
    return [
        (new Metrics\VisitorsPerDay)->width('1/3'),
    ];
}

打开Nova后台,你将看到牛逼的东西来了:

是不是很神奇,好了,我们继续用下面的命令创建另外几个东西:

php artisan nova:value NewVisitors
php artisan nova:partition VisitorsPerStatus

参照VisitorsPerDay修改上面两个文件,注意一下VisitorsPerStatus里面有一个grouByColoum我们要替换成status,什么意思?字面意思!
搞完了,在修改cards方法:

public function cards(Request $request)
{
    return [
        (new Metrics\VisitorsPerDay)->width('1/3'),
        (new Metrics\NewVisitors)->width('1/3'),
        (new Metrics\VisitorsPerStatus)->width('1/3'),
    ];
}

生成的图形如下:

metrics

怎么样?是不是简单到爆炸!!

让我们继续下一个功能吧!

Filters

这个是干嘛的呢?字如其意,我不会写教程,直接搞完了看图说话,你们就明白了!

php artisan nova:filter VisitorType
php artisan nova:filter VisitorStatus

看你的Nova目录下是不是又多了一个文件夹Filters,我们在里面加一些查询吧。
分别修改两个文件,为了方便我都放一起了:

// 这是VisitorStatus
public function apply(Request $request, $query, $value)
{
    return $query->where('status', $value);
}
public function options(Request $request)
{
    return \App\Visitor::getStatuses();
}
// 这是VisitorType
public function apply(Request $request, $query, $value)
{
    return $query->where('status', $value);
}

public function options(Request $request)
{
    return \App\Visitor::getTypes();
}

搞定后进入Visitor注册Filters。

public function filters(Request $request)
{
    return [
        new Filters\VisitorStatus,
        new Filters\VisitorType,
    ];
}

弄完回去看一眼,你就可以发现这个过滤器了:

filter

牛逼吧,是不是好简单!!
让我们继续吧!

Lenses

这又是个啥?你可以这样理解他就是一个更高级的filter,可以定制一些eloquent。看下面的代码示例吧!
比如我们现在要查修哪些访问时间密集的客户要怎么做呢?
先创建一个lens

php artisan nova:lens TimeIntensiveVisitors

修改lenses,添加一些查询语句:

public static function query(LensRequest $request, $query)
{
    return $request->withOrdering($request->withFilters(
        $query->select([
        'visitors.id',
        'visitors.name',
        'visitors.email',
        'visitors.status',
        'visitors.type',
        DB::raw('count(notes.id) as Count')
    ])
    ->join('notes', 'visitors.id', '=', 'notes.visitor_id')
    ->orderBy('Count', 'desc')
    ->groupBy('visitors.id', 'visitors.name')
    ));
}

添加完这个后,我们再继续在field里面添加哪些需要显示的:

 public function fields(Request $request)
{
    return [
        ID::make('ID', 'id')->sortable(),
        Text::make('Name')->sortable(),
        Select::make('Status')->sortable(),
        Text::make('Type')->sortable(),
        Text::make('Email')->sortable(),
        Number::make('Notes Count', 'Count'),
    ];
}

注意,不要忘记在开头use一下这些字段。

use App\Nova\Filters\VisitorStatus;
use App\Nova\Filters\VisitorType;
use Laravel\Nova\Fields\ID;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Fields\Select;
use Laravel\Nova\Fields\Number;
use Laravel\Nova\Lenses\Lens;
use Laravel\Nova\Http\Requests\LensRequest;

接下来,我们将注册我们的自定义过滤器,以便我们可以像过去那样操纵结果,lenses可以随意用我们之前定义的过滤器。

public function filters(Request $request)
{
    return [
        new VisitorStatus,
        new VisitorType,
    ];
}

完事了吗,不,还没完呢,还要在我们的Visitor Recource里面引用一下。

public function lenses(Request $request)
{
    return [
        new Lenses\TimeIntensiveVisitors,
    ];
}

好了,终于完事了,打开nova后台看看,什么样的结果。


总结

怎么样,通过这个例子是不是明白了nova的一些基本的操作方法了,还有些别的我们下次再写吧。这是nova系列的第二篇。
所有代码我都上传到了:我的github了!
后续我将扩展下这个例子,继续再搞一篇nova,与君共勉!

深入Laravel Nova教程(二)
Tagged on:     

One thought on “深入Laravel Nova教程(二)

Leave a Reply

Your email address will not be published. Required fields are marked *