ariby/update-status-by-time

This package help you to select data that are matched with your Input db column for different time interval.


License
MIT

Documentation

資料庫時間間隔查詢器

##使用情境 此 package 之使用情境為,假設今有一 Table movies:

/*
|--------------------------------------------------------------------------
| movies
|--------------------------------------------------------------------------
|
| 記錄目前所有電影的資訊,欄位如下:
|
*/
|--------------------------------------------------------------------------
| id  |   name   |   status    |   startTime    |    endTime ...
|--------------------------------------------------------------------------
|  0  | 哈利波特  |    已下檔    |  2018-02-01    |   2018-02-01 ...
|--------------------------------------------------------------------------
|  1  | 與神同行  |    上映中    |  2018-04-01    |   2018-04-15 ...
|--------------------------------------------------------------------------
|  2  | 玩命關頭  |    上映中    |  2018-04-10    |   2018-04-30 ...
|--------------------------------------------------------------------------
|  3  | 全面啟動  |    未上映    |  2018-04-16    |   2018-05-30 ...
|--------------------------------------------------------------------------
|  4  | 明天過後  |    未上映    |  2018-06-01    |   2018-08-01 ...
|--------------------------------------------------------------------------
| ... |   ...    |    ...       |       ...     |    ...

假設今天日期是 2018-04-16,那麼我們會發現,在資料庫中
id=1 的與神同行的 "status" 應該被更新為"已下檔"
id=3 的明天過後的 "status" 應該被更新為"上映中"

這個 package 的功能就是為你找出那些 "應該被更新的 row id"
你可以依據不同的輸入來做限制,類似的使用有文章定時發佈與隱藏等...
再配合 Laravel 的排程使用,便可以使程式依自已所需要的頻率,"定期的自動更新維護 table 資訊"

安裝

可以透過下列指令進行安裝

composer require ariby/update-status-by-time

並在config/app.php加上Provider

'providers' => [
    ...
    Ariby\UpdateStatusByTime\UpdateStatusByTimeServiceProvider::class,
],    

然後在要使用的地方上方,加上下方程式碼做 include

use Ariby\UpdateStatusByTime\UpdateStatusByTime;

使用方法

目前的 Method 一共有 3 個,以查詢的時間來做區分
分別是 checkBefore、checkBetween、checkAfter 且都必須傳入四個參數,依序分別是:

$tableName => 欲查詢的table名稱,以上例就是"movies"

$tag => Array,欲做比對的欄位與其值,以movis為例 => ['status' => '上映中']
['status' => ['上映中', '已下檔']]` // 相當於 `status != "上映中" && status != "已下檔"

$columnName => Array,存入table中的column欄位名稱,實際格式如下
['primaryKey' => "table中欲回傳的欄位名稱,如id",
'start_at'   => 'table中記錄判斷起始時間的欄位名稱',
'end_at'     => 'table中記錄判斷結束時間的欄位名稱'
]`

其中:
checkBefore 可不傳入 end_at
checkAfter 可不傳入 start_at
checkBetween 則start_at與end_at皆必須傳入

$function => 為一閉包函式,應接收一陣列參數,會包含查詢結果,可執行使用者想做之事,比如更新欄位

以下為以 movies 為使用案例的程式

checkBefore

UpdateStatusByTime::checkBefore("movies",
['status' => '未上映'], 
array('primaryKey'=>'id', 'start_at'=>'startTime'),
function($array){ ...do something what you want to do });

在 function 結束時,程式會自動將查詢結果帶入閉包函式執行,回傳結果如下:

array(); // 以上表 movies 為例,無欄位應該做修改

使用者應在閉包函式做欲做之事,如將 status 欄位修改為「未上映」,以 Laravel ORM 為例

Movies::whereIn('id', $array)->update['status'=>'未上映'];

checkBetween

$result = UpdateStatusByTime::checkBetween("movies",
['status' => '上映中'], 
array('primaryKey'=>'id', 'start_at'=>'startTime', 'end_at' => 'endTime'),
function($array){ ...do something what you want to do });

在 function 結束時,程式會自動將查詢結果帶入閉包函式執行
回傳結果如:

array(3); // 以上表 movies 為例,id=3 之資料應該被修改

使用者應在閉包函式做欲做之事,如將 status 欄位修改為「上映中」,以 Laravel ORM 為例

Movies::whereIn('id', $array)->update['status'=>'上映中'];

checkAfter

$result = UpdateStatusByTime::checkAfter("movies",
['status' => '已下檔'], 
array('primaryKey'=>'id', 'end_at' => 'endTime'),
function($array){ ...do something what you want to do });

在 function 結束時,程式會自動將查詢結果帶入閉包函式執行
回傳結果如:

array(1); // 以上表 movies 為例,id=3 之資料應該做修改

使用者應在閉包函式做欲做之事,如將 status 欄位修改為「上映中」,以 Laravel ORM 為例

Movies::whereIn('id', $array)->update['status'=>'已下檔'];

回傳格式

function 會自動執行閉包函式,並將查詢結果之 id 陣列以參數傳入
當執行發生錯誤時,呼叫函式實際回傳格式如下:

array(
    'ok'   => 'false',     // 回傳"true" or "false" 代表是否發生錯誤
    'msg'  => 'error msg', // 錯誤訊息,比如參數錯誤、table不存在..etc
    'data' => array()      // 查詢結果之id陣列
)

可以下方程式來判斷是否發生錯誤

if( $returnArray['ok'] != 'true' ){
    // error
}else{
    // right
}

擴充案例

若你的案例可能會有「沒有預定結束時間」的狀況 (即 end_at === null),且你使用的是 Laravel 專案,可以在 Model 加上以下程式碼:

public function setPublishEndAtAttribute($publish_end_at){
    if(is_null($publish_end_at)){
        $this->attributes['publish_end_at'] = '9999-12-31 23:59:59';
    }else{
        $this->attributes['publish_end_at'] = $publish_end_at;
    }
}

public function getPublishEndAtAttribute($publish_end_at){
    if($publish_end_at == '9999-12-31 23:59:59'){
        return null;
    }else{
        return $publish_end_at;
    }
}

使用Artisan命令列做查詢

在config/app.php加上服務提供者後,可直接以php artisan使用

\\ php artisan可看到
TimeSelect:getSatisfyIDs    {tableName : The name of table.}
                       {method : 1:before, 2:now, 3:after}
                       {primaryKey : The column you want to return after search.}
                       {beforeTimeKey : The column name in the database.}
                       {afterTimeKey : The column name in the database.}
                       {tagArray* : The rule that you want to set. The first parameter is key, and after is value. e.g. send "status false tag error" means ["staus" => "false", "tag" => "error"]}

以下為使用範例:

// 輸入指令
php artisan TimeSelect:getSatisfyIDs movies 2 id startTime endTime status 未上映
// 回傳Number為滿足條件的row數量、result為滿足的id們,以" ,"為分隔符
Number: 1
result: 3

配合Laravel排程使用

可以參考 Laravel 官方文件

以下是範例程式
App/console/kerner.php

// console/Kernel.php
    
    ...
    protected $commands = [
        \App\Console\Commands\UpdateMoviesStatus::class,
    ];
    ...
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('update:movies')->everyMinute();
    }
    ...

// console/commands/updateMoviesStatus.php

App\Console\Commands建立updateMoviesStatus.php

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

use App\Models\Movies;
use Ariby\UpdateStatusByTime\UpdateStatusByTime;

class UpdateMoviesStatus extends Command
{
    // 命令名稱
    protected $signature = 'update:movies';

    // 說明文字
    protected $description = '[update] Movies status';

    public function __construct()
    {
        parent::__construct();
    }

    // Console 執行的程式
    public function handle()
    {
        /* before-檢查未上映的電影並更新 */
        UpdateStatusByTime::checkBefore("movies",['status' => '未上映'], array('primaryKey'=>'id', 'start_at'=>'startTime'), function($array){
            if(!is_null($array))
                Movies::whereIn('id', $array)->update(['status' => '未上映', 'stage' => 'Before']);
        });

        /* now-檢查上映中的電影並更新 */
        UpdateStatusByTime::checkBetween("movies",['status' => '上映中'], array('primaryKey'=>'id', 'start_at'=>'startTime', 'end_at'=>'endTime'), function($array){
            if(!is_null($array))
                Movies::whereIn('id', $array)->update(['status' => '上映中', 'stage' => 'Now']);
        });

        /* after-檢查已下檔的電影並更新 */
        UpdateStatusByTime::checkAfter("movies",['status' => '已下檔'], array('primaryKey'=>'id', 'end_at'=>'endTime'), function($array){
            if(!is_null($array))
                Movies::whereIn('id', $array)->update(['status' => '已下檔', 'stage' => 'After']);
        });
    }
}

接著執行php artisan schedule:run,便可使排程每分鐘檢查資料庫中電影狀態並更新

注意:若你是 windows ,執行 php artisan schedule:run 可能會遇到 ( windows 跑 Laravel 排程的問題)

artisan" update:movies > "NUL" 2>&1

可參考 https://github.com/laravel/framework/issues/7868 或其他方式解決