深入 Laravel Nova 教程例項

leienshu發表於2019-01-23

在我的上一篇文章我簡單的介紹了一下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:leads,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管理員介面,我們可以看到索引已經自行構建,我們的表單現在可用了!


現在訪問者可以提交資料,我們的團隊可以上傳他們的潛在客戶!如果我們能夠跟蹤管理員使用者在Visitors和Notes上為管理目的所做的任何編輯或更改,那將是很好的。 很幸運的是,Nova讓這變得非常容易。

我們只需要將 Actionable trait新增到模型中,我們將獲得在模型上一些列執行的操作列表。


//In app\Visitor.php 和 app\Note.php

use Actionable;

好了,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'),

    ];

}

生成的圖形如下:

怎麼樣?是不是簡單到爆炸!!

讓我們繼續下一個功能吧!

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,

    ];

}

弄完回去看一眼,你就可以發現這個過濾器了:

牛逼吧,是不是好簡單!!

讓我們繼續吧!

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,與君共勉!

求知若飢,虛心若愚!

相關文章