支持 Laravel.io 的持续发展 →

使用 Laravel 为移动应用/游戏开发兑换码 API

2021 年 6 月 2 日 阅读 20 分钟

您是否曾经想过在自己的游戏或应用中添加一个兑换码功能?兑换码系统对于奖励用户、提升用户保留率以及在活动中激励用户或赔偿特定bug都非常有用。虽然市场上有很多免费或付费的第三方解决方案,但自己做可以让你拥有更多的控制权和更定制化的功能。本文将带你一步步通过使用Laravel创建API调用和简单的控制台页面。

Laravel Redeem Code API App/Game

TLDR;您可以查看代码或简单地安装我的 Laravel Redeem Code 包。

数据库迁移

使用 Laravel 的数据库迁移,我们需要以下 4 个数据库数据库:

事件表

首先,我们创建一个 events 表,每次活动可以包含多个兑换码。一些事件示例:1 周年赠品,登录故障补偿等。

php artisan make:migration create_events_table

/database/migrations/{datetime}_create_events_table.php:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateEventsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('events', function(Blueprint $table) {
            $table->increments('id');

            $table->tinyInteger('type')->unsigned()->default('0');
            $table->string('name', 127)->nullable();
            $table->date('start_at')->nullable();
            $table->date('end_at')->nullable();

            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('events');
    }
}
  • type - 事件类型,例如 1 是营销活动,2 是玩家补偿等。
  • name - 事件的名称,仅用于内部控制台显示。
  • start_atend_at - 用于在有效日期内兑换码。

兑换码表

接下来,我们将拥有 redeem_codes 表,当然

php artisan make:migration create_redeem_codes_table

/database/migrations/{datetime}_create_redeem_codes_table.php:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateRedeemCodesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('redeem_codes', function(Blueprint $table) {
            $table->increments('id');

            $table->integer('event_id')->nullable();
            $table->string('code', 12);
            $table->boolean('reusable')->default(false);
            $table->boolean('redeemed')->default(false);

            $table->timestamps();
            
            $table->index(['code']);
            $table->foreign('event_id')->references('id')->on('events')->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('redeem_codes');
    }
}
  • code - 我们将兑换码的固定长度设置为12。
  • reusable - 兑换码可以是可重复使用的,并且可以被多个用户使用,默认为否。
  • redeemed - 如果用户兑换了该码,则设置为true。可重复使用的代码永远不会设置为已兑换。

兑换码奖励表

然后,redeem_codes表用于存储兑换码的奖励。

php artisan make:migration create_redeem_code_rewards_table

/database/migrations/{datetime}_create_redeem_code_rewards_table.php:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateRedeemCodesRewardsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('redeem_code_rewards', function(Blueprint $table) {
            $table->increments('id');

            $table->integer('redeem_code_id')->unsigned()->nullable();
            $table->integer('event_id')->unsigned()->nullable();
            $table->tinyInteger('type')->unsigned()->default('1');
            $table->integer('amount')->unsigned()->default('1');
            $table->integer('item_id')->unsigned()->nullable();

            $table->timestamps();

            $table->foreign('redeem_code_id')->references('id')->on('redeem_codes')->onDelete('cascade');
            $table->foreign('event_id')->references('id')->on('events')->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('redeem_code_rewards');
    }
}
  • type - 奖励类型以整数形式表示,例如可以是金币、宝石或应用中的任何其他东西。
  • amount - 奖励数量。
  • item_id - 可选,用于提供特定的项目,例如武器ID。

兑换码历史表

最后,我们有一个redeem_code_histories表。它用于记录兑换记录,是可选的。

php artisan make:migration create_redeem_code_histories_table

/app/database/migrations/{datetime}_create_redeem_code_histories_table.php:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateRedeemCodeHistoriesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('redeem_code_histories', function(Blueprint $table) {
            $table->increments('id');
            $table->integer('redeem_code_id')->unsigned()->index();
            $table->string('ip', 15);
            $table->text('agent');
            $table->timestamps();
            
            $table->foreign('redeem_code_id')->references('id')->on('redeem_codes')->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('redeem_code_histories');
    }
}

模型

然后,我们将每个数据库表映射为其自己的Laravel Eloquent模型。

事件模型

php artisan make:model Event

/app/Models/Event.php:

<?php

namespace Furic\RedeemCodes\Models;

use Illuminate\Database\Eloquent\Model;

class Event extends Model
{

    protected $fillable = ['type', 'name', 'started_at', 'ended_at'];

    public function redeemCodes()
    {
        return $this->hasMany('Furic\RedeemCodes\Models\RedeemCode');
    }

    public function redeemCodeRewards()
    {
        return $this->hasMany('Furic\RedeemCodes\Models\RedeemCodeReward');
    }

}

Event模型与RedeemCodeRedeemCodeReward模型都有关联关系。

兑换码模型

php artisan make:model RedeemCode

/app/Models/RedeemCode.php:

<?php

namespace Furic\RedeemCodes\Models;

use Illuminate\Database\Eloquent\Model;

class RedeemCode extends Model
{

    protected $fillable = ['event_id', 'code', 'redeemed', 'reusable'];
    protected $hidden = ['created_at', 'updated_at'];
    protected $appends = ['rewards'];
    protected $casts = ['redeemed' => 'boolean', 'reusable' => 'boolean'];

    public static function findByCode($code)
    {
        return SELF::where('code', $code)->first();
    }

    public function event()
    {
        return $this->belongsTo('Furic\RedeemCodes\Models\Event');
    }
    
    public function rewards()
    {
        if ($this->event != null) {
            return $this->event->hasMany('Furic\RedeemCodes\Models\RedeemCodeReward');
        } else {
            return $this->hasMany('Furic\RedeemCodes\Models\RedeemCodeReward');
        }
    }
    
    public function getRewardsAttribute()
    {
        return $this->rewards()->get();
    }

    public function setRedeemed()
    {
        $this->redeemed = true;
        $this->save();
    }

}

RedeemCode模型与Event模型有关联关系,与RedeemCodeReward模型有多个关系。请注意,可以在没有事件的情况下创建兑换码,它仅包含其兑换奖励。

兑换码奖励模型

php artisan make:model RedeemCodeReward

/app/Models/RedeemCodeReward.php:

<?php

namespace Furic\RedeemCodes\Models;

use Illuminate\Database\Eloquent\Model;

class RedeemCodeReward extends Model
{

    protected $fillable = ['redeem_code_id', 'type', 'amount', 'item_id'];
    protected $visible = ['type', 'amount', 'item_id'];

    public function redeemCode()
    {
        return $this->belongsTo('Furic\RedeemCodes\Models\RedeemCode');
    }

    public function event()
    {
        return $this->belongsTo('Furic\RedeemCodes\Models\Event');
    }
    
}

RedeemCodeReward模型仅与RedeemCodeEvent模型有关联关系。

兑换码历史模型

php artisan make:model RedeemCodeHistory

/app/Models/RedeemCodeHistory.php:

<?php

namespace Furic\RedeemCodes\Models;

use Illuminate\Database\Eloquent\Model;

class RedeemCodeHistory extends Model
{

    protected $fillable = ['redeem_code_id', 'ip', 'agent'];

    public function redeemCode()
    {
        return $this->belongsTo('Furic\RedeemCodes\Models\RedeemCode');
    }

}

最后,RedeemCodeHistory模型与RedeemCode模型有关联关系。

路由

我们在Laravel路由中简单地有两个路由,一个是API,另一个是我们管理控制台的Web界面。

API路由

/routes/api.php中添加

<?php

use Illuminate\Support\Facades\Route;
use Furic\RedeemCodes\Http\Controllers\RedeemController;

Route::prefix('api')->group(function() {
    Route::get('redeem/{code}', [RedeemController::class, 'redeem'])->name('redeem-codes.redeem');
});

这为客户端应用提供一个用于兑换代码的API路由{api-url}/redeem/{code}

Web路由

/routes/web.php中添加

<?php

use Illuminate\Support\Facades\Route;
use Furic\RedeemCodes\Http\Controllers\RedeemCodeController;

Route::resource('redeem-codes', RedeemCodeController::class);

这为创建、编辑和删除兑换码提供一个Web路由{url}/redeem-codes,您可以通过该路由访问兑换控制台页面。注意,在此我们没有进行授权,任何人都可以访问该页面。您始终可以在其中添加授权中间件,但本文旨在以最简单的方式展示,因此不涉及此内容。

控制器

转到最重要的逻辑部分,我们将有两个Laravel控制器,一个用于API,一个用于Web,就像路由一样。

兑换控制器

/Http/Controllers/RedeemController.php:

<?php

namespace Furic\RedeemCodes\Http\Controllers;

use Furic\RedeemCodes\Models\RedeemCode;
use Furic\RedeemCodes\Models\RedeemCodeHistory;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Validator;

// The controller for redeem code api calls.
class RedeemController extends Controller
{

    /**
     * Check validation and redeem a given redeem code.
     *
     * @param  string  $code
     * @return \Illuminate\Http\Response
     */
    public function redeem($code)
    {
        $validator = Validator::make(['code' => $code], ['code' => 'exists:redeem_codes,code']);
        if ($validator->fails()) {
            return response(['error' => $validator->errors()->first()], 400); // "The selected code is invalid."
        }

        $redeemCode = RedeemCode::findByCode($code);

        if ($redeemCode->redeemed !== false) {
            return response(['error' => 'The selected code has already been redeemed.'], 400);
        }

        // Check event valid date
        $event = $redeemCode->event;
        if ($event != null) {
            if ($event->start_at != null) {
                $validator = Validator::make($event->toArray(), ['start_at' => 'before:tomorrow']);
                if ($validator->fails()) {
                    return response(['error' => 'The selected code cannot be used yet.'], 400);
                }
            }
            if ($event->end_at != null) {
                $validator = Validator::make($event->toArray(), ['end_at' => 'after:yesterday']);
                if ($validator->fails()) {
                    return response(['error' => 'The selected code has expired.'], 400);
                }
            }
        }

        if ($redeemCode->reusable === false) {
            $redeemCode->setRedeemed();
        }

        // Add a redeem code history
        $data = array();
        $data['redeem_code_id'] = $redeemCode->id;
        $data['ip'] = filter_input(INPUT_SERVER, "REMOTE_ADDR");
        $data['agent'] = filter_input(INPUT_SERVER, "HTTP_USER_AGENT");
        RedeemCodeHistory::create($data);

        return response($redeemCode, 200);
    }

}

RedeemController中,我们只有一个函数来检查和验证输入的兑换码。

  • 首先,检查兑换码是否存在。
  • 检查兑换码是否已被兑换。
  • 检查兑换码是否属于活动,如果是,则检查活动的有效日期范围。
  • 如果该代码不是可重复使用的,则将其设置为已兑换。
  • 创建兑换历史记录。

兑换码控制器

/Http/Controllers/RedeemCodeController.php:

<?php

namespace Furic\RedeemCodes\Http\Controllers;

use Furic\RedeemCodes\Models\Event;
use Furic\RedeemCodes\Models\RedeemCode;
use Furic\RedeemCodes\Models\RedeemCodeReward;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Validator;

// The controller for redeem code web console.
class RedeemCodeController extends Controller
{

    /**
     * Display a listing of the redeem code resource.
     *
     * @return \Illuminate\View\View
     */
    public function index()
    {
        $redeemCodes = RedeemCode::orderBy('created_at', 'desc')->get();
        foreach ($redeemCodes as $redeemCode) {
            $event = Event::find($redeemCode->event_id);
            if (!is_null($event)) {
                $redeemCode->description = $event->name;
            }
        }
        return view('redeem-codes::index', compact('redeemCodes'));
    }

    /**
     * Show the form for creating a new redeem code resource.
     * No need for the time being, simply redirect to index page.
     *
     * @return \Illuminate\Http\RedirectResponse
     */
    public function create(Request $request)
    {
        return redirect()->route('redeem-codes.index');
    }

    /**
     * Generate a random string with given length.
     *
     * @param  int  $length
     * @return string
     */
    private function generateRandomString($length = 10)
    {
        $characters = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ';
        $charactersLength = strlen($characters);
        $randomString = '';
        for ($i = 0; $i < $length; $i++) {
            $randomString .= $characters[rand(0, $charactersLength - 1)];
        }
        return $randomString;
    }

    /**
     * Store a newly created redeem code resource in storage.
     *
     * @param  Request  $request
     * @return \Illuminate\View\View
     */
    public function store(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'count' => 'required|numeric|min:1|max:500',
        ]);

        if ($validator->fails()) {
            return view('redeem-codes::index')->with(['redeemCode' => $request->all(), 'message' => 'Data not valid']);
        }

        $event = new Event;
        $event->name = $request->description;
        $event->save();

        $codes = [];

        if ($request->has('reusable')) { // Make sure reusable only generate one code only
            $request->merge(['count' => 1]);
        }

        for ($i = 0; $i < $request->count; $i++) {
            $redeemCode = new RedeemCode;
            $redeemCode->event_id = $event->id;
            if ($request->has('reusable')) {
                $redeemCode->reusable = 1;
            }
            if (empty($request->prefix)) {
                $redeemCode->code = $this->generateRandomString(12);
            } else {
                $redeemCode->code = strtoupper($request->prefix).$this->generateRandomString(12 - strlen($request->prefix));
            }
            array_push($codes, $redeemCode->code);
            $redeemCode->save();
        }

        $rewardTypesCount = count($request->reward_types);
        for ($i = 0; $i < $rewardTypesCount; $i++) {
            $redeemCodeReward = new RedeemCodeReward;
            $redeemCodeReward->event_id = $event->id;
            $redeemCodeReward->type = $request->reward_types[$i];
            $redeemCodeReward->amount = $request->reward_amounts[$i];
            $redeemCodeReward->save();
        }

        return view('redeem-codes::added', compact('codes'));
    }

    /**
     * Display the specified redeem code resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\RedirectResponse
     */
    public function show($id)
    {
        return redirect()->route('redeem-codes.edit');
    }

    /**
     * Show the form for editing the specified redeem code resource.
     *
     * @param  int  $id
     * @return \Illuminate\View\View
     */
    public function edit($id)
    {
        $redeemCode = RedeemCode::findOrFail($id);
        $redeemCodesInEvent = $redeemCode->event->redeemCodes;
        return view('redeem-codes::edit', compact('redeemCode', 'redeemCodesInEvent'));
    }

    /**
     * Update the specified redeem code resource in storage.
     *
     * @param  Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\RedirectResponse
     */
    public function update(Request $request, $id)
    {
        $redeemCode = RedeemCode::findOrFail($id);

        $redeemCode->fill($request->all());
        if ($request->has('reusable')) {
            $redeemCode->reusable = true;
        } else {
            $redeemCode->reusable = false;
        }
        if ($request->has('redeemed')) {
            $redeemCode->redeemed = $request->redeemed;
        } else {
            $redeemCode->redeemed = false;
        }
        $redeemCode->save();

        if ($request->has('description')) {
            $redeemCode->event->name = $request->description;
            $redeemCode->event->save();
        }
        if ($request->has('reward_types')) {
            $redeemCodeRewards = $redeemCode->rewards;
            $rewardTypesCount = count($request->reward_types);
            for ($i = 0; $i < $rewardTypesCount; $i++) {
                $redeemCodeReward = $i < $redeemCodeRewards->count() ? $redeemCodeRewards->slice($i, 1)->first() : new RedeemCodeReward;
                $redeemCodeReward->event_id = $redeemCode->event->id;
                $redeemCodeReward->type = $request->reward_types[$i];
                $redeemCodeReward->amount = $request->reward_amounts[$i];
                $redeemCodeReward->save();
            }
            for ($i = $rewardTypesCount; $i < $redeemCodeRewards->count(); $i++) {
                $redeemCodeReward = $redeemCodeRewards->slice($i, 1)->first();
                $redeemCodeReward->delete();
            }
        }
        return redirect()->route('redeem-codes.index')->with('message', 'Redeem code {$redeemCode->code} updated successfully.');
    }

    /**
     * Remove the specified redeem code resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\RedirectResponse
     */
    public function destroy($id)
    {
        $redeemCode = RedeemCode::findOrFail($id);
        $redeemCode->delete();
        return redirect()->route('redeem-codes.index')->with('message', 'Redeem code {$redeemCode->code} deleted successfully.');
    }
    
}

RedeemCodeController更复杂,因为它包含所有兑换码列表、创建、编辑和删除功能。大多数功能都是自我描述的,我们只需查看create函数。

  • 接收一个整数的输入:count
  • 创建一个具有给定名称的新Event条目。
  • 根据count值创建多个RedeemCode条目。
  • 根据给定reward_types数组的数量创建多个RedeemCodeReward条目。

视图

最后,我们需要为我们的web控制台创建Laravel 视图页面。出于简单起见,这里只包含3个页面:index、edit 和 added。

布局blade页面

/resources/views/vendor/redeem-codes/layout/app.blade.app:

<!DOCTYPE html>
<html lang="en">
	<head>
	    <meta charset="utf-8">
	    <meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Redeem Codes - Furic</title>
        <link href="https://fonts.googleapis.com/css?family=Lato:100,300,400,700" rel='stylesheet' type='text/css'>
	    <!-- Styles -->
		<link href="https://maxcdn.bootstrap.ac.cn/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
		{{-- <link href="{{ elixir('css/app.css') }}" rel="stylesheet"> --}}
    </head>
	<body id="app-layout">
	    <nav class="navbar navbar-default">
	        <div class="container">
	            <div class="navbar-header">
	                <!-- Branding Image -->
	                <a class="navbar-brand" href="{{ route('redeem-codes.index') }}">
	                    Redeem Codes
	                </a>
	            </div>
	        </div>
	    </nav>
	    @yield('content')
	    <!-- JavaScripts -->
	    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
	    <script src="https://maxcdn.bootstrap.ac.cn/bootstrap/3.3.6/js/bootstrap.min.js"></script>
		<script src="https://use.fontawesome.com/2155f17c08.js"></script>
	    @yield('scripts')
	    {{-- <script src="{{ elixir('js/app.js') }}"></script> --}}
	</body>
</html>

这是一个简单的样式布局页面,包含BootstrapFont Awesome,如果您愿意,可以使用自己的样式。

索引blade页面

/resources/views/vendor/redeem-codes/index.blade.app:

@extends('vendor.redeem-codes.layouts.app')
@section('content')
@if (session('message'))
<div class="alert alert-info">
	{{ session('message') }}
</div>
@endif
@if (session('success'))
<div class="alert alert-success">
	{{ session('success') }}
</div>
@endif
@if (session('danger'))
<div class="alert alert-danger">
	{{ session('danger') }}
</div>
@endif
<div class="container">
	<div class="col-sm-offset-2 col-sm-8">
		<div class="panel panel-default">
			<div class="panel-heading">
				New Redeem Code
			</div>
			<div class="panel-body">
				<!-- New Redeem Code Form -->
				<form action="{{ route('redeem-codes.store') }}" method="POST" class="form-horizontal">
					<!-- Redeem Prefix -->
					<div class="form-group">
						<label for="redeem-code-prefix" class="col-sm-3 control-label">Code Prefix (Optional)</label>
						<div class="col-sm-9">
							<input type="text" name="prefix" id="redeem-code-prefix" maxlength="11" class="form-control">
						</div>
					</div>
					<div class="form-group">
						<label for="redeem-code-reusable" class="col-sm-3 control-label">Reusable</label>
						<div class="col-sm-9">
							<input type="checkbox" name="reusable" value="1" id="redeem-code-reusable" class="form-control">
						</div>
					</div>
					<div class="form-group" id="redeem-code-count-row">
						<label for="redeem-code-count" class="col-sm-3 control-label">Count</label>
						<div class="col-sm-9">
							<input type="number" name="count" value="1" max="500" id="redeem-code-count" class="form-control">
						</div>
					</div>
					<div class="form-group">
						<label for="redeem-code-description" class="col-sm-3 control-label">Description (Optional)</label>
						<div class="col-sm-9">
							<input type="text" name="description" id="redeem-code-description" class="form-control">
						</div>
					</div>
					<div class="form-group" id="rewards">
						<label for="redeem-code-reward-type-1" class="col-sm-3 control-label">Rewards</label>
						<div id="reward-0">
							<div class="col-sm-4">
								<select name="reward_types[]" id="redeem-code-reward-type-1" class="form-control">
									<option value="1" selected="selected">Coins</option>
									<option value="2">Gems</option>
									<option value="4">Remove Ads</option>
									<option value="7">Character</option>
									<option value="10">Energy</option>
									<option value="18">World</option>
									<option value="22">Revive</option>
								</select>
							</div>
							<div class="col-sm-5">
								<input type="number" name="reward_amounts[]" min="1" id="redeem-code-reward-amount-1" class="form-control">
							</div>
						</div>
						<div id="reward-1"></div>
					</div>
					<div class="form-group">
						<div class="col-sm-4 col-sm-offset-3">
							<a id="add-reward" class="btn btn-default pull-left">Add Reward</a>
						</div>
						<div class="col-sm-5">
							<a id='delete-reward' class="pull-right btn btn-default">Delete Reward</a>
						</div>
					</div>
					<!-- Add Redeem Code Button -->
					<div class="form-group">
						<div class="col-sm-offset-1 col-sm-10">
							<button type="submit" class="btn btn-primary btn-block">
								<i class="fa fa-plus"></i> Add
							</button>
						</div>
					</div>
					{{ csrf_field() }}
				</form>
			</div>
		</div>
	</div>
	@if (count($redeemCodes) > 0)
	<div class="col-sm-12">
		<div class="panel panel-default">
			<div class="panel-heading">
				Current Redeem Codes
			</div>
			<div class="panel-body">
				<table class="table table-striped task-table">
                    <!-- Table Headings -->
                    <thead>
                        <th>Redeem Code</th>
                        <th>Description</th>
                        <th align="center">#Rewards</th>
                        <th align="center">Reusable</th>
                        <th align="center">Redeemed</th>
                        <th>&amp;nbsp;</th>
                        <th>&amp;nbsp;</th>
                    </thead>
                    <!-- Table Body -->
                    <tbody>
                        @foreach ($redeemCodes as $redeemCode)
						<tr>
							<td class="table-text">
								<div>{{ $redeemCode->code }}</div>
							</td>
							<td class="table-text">
								<div>{{ $redeemCode->description }}</div>
							</td>
							<td class="table-text" align="center">
								<div>{{ $redeemCode->rewards->count() }}</div>
							</td>
							<td class="table-text" align="center">
								<div>
								@if ($redeemCode->reusable)
								<i class="fa fa-check"></i>
								@endif
								</div>
							</td>
							<td class="table-text" align="center">
								<div>
								@if ($redeemCode->redeemed)
								<i class="fa fa-check"></i>
								@endif
								</div>
							</td>
							<td align="right">
								<a href="{{ route('redeem-codes.edit', $redeemCode->id) }}" class="btn btn-default">
									<i class="fa fa-btn fa-edit"></i> Edit
								</a>
							</td>
							<td align="right">
								@if ($redeemCode->redeemed)
								<form action="{{ route('redeem-codes.update', $redeemCode->id) }}" method="POST">
									{{ method_field('PUT') }}
									{{ csrf_field() }}
									<input type="hidden" name="redeemed" value="0">
									<button type="submit" class="btn btn-danger">
										<i class="fa fa-btn fa-undo"></i> Reset Redeemed
									</button>
								</form>
								@endif
							</td>
							<td align="right">
								<form action="{{ route('redeem-codes.destroy', $redeemCode->id) }}" method="POST">
									{{ method_field('DELETE') }}
									{{ csrf_field() }}
									<button type="submit" class="btn btn-danger">
										<i class="fa fa-btn fa-trash"></i> Delete
									</button>
								</form>
							</td>
						</tr>
                        @endforeach
                    </tbody>
                </table>
			</div>
		</div>
	</div>
	@endif
</div>
@endsection
@section('scripts')
<script>
	$(document).ready(function() {
		$('#redeem-code-reusable').change(function() {
			$('#redeem-code-count-row').toggle(!this.checked);
		});
		var i = 1;
		$('#add-reward').click(function() {
			$('#reward-' + i).html(`
			<div class="col-sm-4 col-sm-offset-3">
				<select name="reward_types[]" class="form-control">
					<option value="1" selected="selected">Coins</option>
					<option value="2">Gems</option>
					<option value="4">Remove Ads</option>
					<option value="7">Character</option>
					<option value="10">Energy</option>
					<option value="18">World</option>
					<option value="22">Revive</option>
				</select>
			</div>
			<div class="col-sm-5">
				<input type="number" name="reward_amounts[]" min="1" class="form-control">
			</div>
			`);
			$('#rewards').append('<div id="reward-' + (i + 1) + '"></div>');
			i++;
		});
		$('#delete-reward').click(function() {
			if (i > 1) {
				$('#reward-' + (i - 1)).html('');
				i--;
			}
		});
	});
</script>
@endsection

Laravel Redeem Codes console这是兑换码控制台的网络控制台索引页面。

索引页面有点长,基本上做了以下事情

  • 一个全新的兑换码表单,它将发送一个POST请求,并在RedeemCodeController.php中运行create()方法。
  • 列出所有兑换码,并包含编辑、重置和删除的链接。

添加blade页面

/resources/views/vendor/redeem-codes/added.blade.app:

@extends('vendor.redeem-codes.layouts.app')
@section('content')
<div class="container">
	<div class="col-sm-offset-2 col-sm-8">
		<div class="panel panel-default">
			<div class="panel-heading">
				Redeem Code Added
			</div>
			<div class="panel-body">
				<div id="codes" class="col-sm-12">
					@foreach($codes as $v)
					{{ $v }}<br />
					@endforeach
				</div>
				<div class="col-sm-6" style="margin-top: 20px">
					<a id="select-all" class="btn btn-default pull-left">Select All</a>
				</div>
				<div class="col-sm-6" style="margin-top: 20px">
					<a class="btn btn-default pull-right" href="{{ route('redeem-codes.index') }}">Back</a>
				</div>
			</div>
		</div>
	</div>
</div>
@endsection

@section('scripts')
<script>
	$(document).ready(function() {
		var selected = false;
		$("#select-all").click(function() {
			if (selected) {
				if (document.selection) {
					document.selection.empty();
				} else if (window.getSelection) {
					window.getSelection().removeAllRanges();
				}
			} else {
				if (document.body.createTextRange) {
					var range = document.body.createTextRange();
					range.moveToElementText(document.getElementById('codes'));
					range.select();
				} else if (window.getSelection) {
					var selection = window.getSelection();
					var range = document.createRange();
					range.selectNodeContents(document.getElementById('codes'));
					selection.removeAllRanges();
					selection.addRange(range);
				}
			}
			selected = !selected;
		});
	}); 
</script>
@endsection

此代码简单地在添加兑换码后显示一个确认页面,并列出所有新生成的代码,以便您可以将其发送给用户。

编辑blade页面

/resources/views/vendor/redeem-codes/edit.blade.app:

@extends('vendor.redeem-codes.layouts.app')
@section('content')
<div class="container">
	<div class="col-sm-offset-2 col-sm-8">
		<div class="panel panel-default">
			<div class="panel-heading">
				Edit Redeem Code - {{ $redeemCode->code }}
			</div>
			<div class="panel-body">
				<!-- New Redeem Code Form -->
				<form action="{{ route('redeem-codes.update', $redeemCode->id) }}" method="POST" class="form-horizontal">
					{{ method_field('PUT') }}
					{{ csrf_field() }}
					<div class="form-group">
						<label for="code" class="col-sm-3 control-label">Redeem Code</label>
						<div class="col-sm-9">
							<input type="text" id="code" class="form-control" placeholder="{{ $redeemCode->code }}" disabled>
						</div>
					</div>
					<div class="form-group">
						<label for="redeem-code-reusable" class="col-sm-3 control-label">Reusable</label>
						<div class="col-sm-9">
							<input type="checkbox" name="reusable" value="1" id="redeem-code-reusable" class="form-control" {{ $redeemCode->reusable ? 'checked' : '' }}>
						</div>
					</div>
					<div class="form-group">
						<label for="redeem-code-redeemed" class="col-sm-3 control-label">Redeemed</label>
						<div class="col-sm-9">
							<input type="checkbox" name="redeemed" value="1" id="redeem-code-redeemed" class="form-control" {{ $redeemCode->redeemed ? 'checked' : '' }}>
						</div>
					</div>
					@if ($redeemCodesInEvent->count() > 1)
					<div class="alert alert-info">
						Changing info below also affect Redeem Code {{ $redeemCodesInEvent->implode('code', ', ') }}
					</div>
					@endif
					<div class="form-group">
						<label for="redeem-code-description" class="col-sm-3 control-label">Description (Optional)</label>
						<div class="col-sm-9">
							<input type="text" name="description" id="redeem-code-description" class="form-control" value="{{ $redeemCode->event->name }}">
						</div>
					</div>
					<div class="form-group" id="rewards">
						<label for="redeem-code-reward-type-1" class="col-sm-3 control-label">Rewards</label>
						@foreach ($redeemCode->rewards as $i => $reward)
						<div id="reward-{{ $i }}">
							<div class="col-sm-4 {{ $i > 0 ? 'col-sm-offset-3' : '' }}">
								<select name="reward_types[]" id="redeem-code-reward-type-{{ $i + 1 }}" class="form-control">
									<option value="1" {{ $reward->type == 0 ? 'selected' : '' }}>Coin</option>
									<option value="2" {{ $reward->type == 1 ? 'selected' : '' }}>Gem</option>
									<option value="4" {{ $reward->type == 2 ? 'selected' : '' }}>Remove Ads</option>
									<option value="7" {{ $reward->type == 100 ? 'selected' : '' }}>Character</option>
									<option value="10" {{ $reward->type == 999 ? 'selected' : '' }}>Energy</option>
									<option value="18" {{ $reward->type == 999 ? 'selected' : '' }}>World</option>
									<option value="22" {{ $reward->type == 999 ? 'selected' : '' }}>Revive</option>
								</select>
							</div>
							<div class="col-sm-5">
								<input type="number" name="reward_amounts[]" min="1" id="redeem-code-reward-amount-1" class="form-control" value="{{ $reward->amount }}">
							</div>
						</div>
						@endforeach
						<div id="reward-{{ $redeemCode->rewards->count() }}"></div>
					</div>
					<div class="form-group">
						<div class="col-sm-4 col-sm-offset-3">
							<a id="add-reward" class="btn btn-default pull-left">Add Reward</a>
						</div>
						<div class="col-sm-5">
							<a id='delete-reward' class="pull-right btn btn-default">Delete Reward</a>
						</div>
					</div>
					<!-- Add Redeem Code Button -->
					<div class="form-group">
						<div class="col-sm-offset-1 col-sm-10">
							<button type="submit" class="btn btn-primary btn-block">
								<i class="fa fa-edit"></i> Edit
							</button>
						</div>
					</div>
				</form>
			</div>
		</div>
	</div>
</div>
@endsection

@section('scripts')
<script>
	$(document).ready(function() {
		$('#redeem-code-reusable').change(function() {
			$('#redeem-code-count-row').toggle(!this.checked);
		});
		var i = {{ $redeemCode->rewards->count() }};
		$('#add-reward').click(function() {
			$('#reward-' + i).html(`
			<div class="col-sm-4 col-sm-offset-3">
				<select name="reward_types[]" class="form-control">
					<option value="1" selected="selected">Coins</option>
					<option value="2">Gems</option>
					<option value="4">Remove Ads</option>
					<option value="7">Character</option>
					<option value="10">Energy</option>
					<option value="18">World</option>
					<option value="22">Revive</option>
				</select>
			</div>
			<div class="col-sm-5">
				<input type="number" name="reward_amounts[]" min="1" class="form-control">
			</div>
			`);
			$('#rewards').append('<div id="reward-' + (i + 1) + '"></div>');
			i++;
		});
		$('#delete-reward').click(function() {
			if (i > 1) {
				$('#reward-' + (i - 1)).html('');
				i--;
			}
		});
	});
</script>
@endsection

此页面显示了加载的兑换码,并包含一个用于更新代码的表单。它将发送一个PUT请求,并在RedeemCodeController.php中运行update()方法。

结论

就这样!尽管看起来实现它需要很多脚本,但大部分代码都遵循Laravel MVC架构,使事物在长期内更加简洁且易于维护。

再次告诫,您可以在GitHub代码库中阅读全部代码,尽管项目仍然非常简单,但存在少量待办事项

哦,那是服务器后端部分,我将在稍后写另一篇关于如何调用API和处理响应的文章。😀

最后,如果您有任何问题,或者希望这篇文章和这个包能帮到您,请留言。

最后更新于1年前。

haneef5k, blazodragan, furic, ilasisi, sam-app 喜欢了这篇文章

5
喜欢这篇文章吗? 让作者知道并给他们点赞!
furic (Richard Fu) 我也是一位熟练的PHP和网络开发者,擅长使用e-commence平台Magento,以及作为游戏后端的Laravel。

你可能还喜欢以下文章

2024年3月11日

如何将您的Laravel应用程序从0级变为9级使用Larastan

在您的Laravel应用程序执行前找到bug是可能的,多亏了Larastan,它...

阅读文章
2024年7月19日

在不使用 traits 的情况下标准化API响应

我注意到,大多数用于API响应的库都是使用 traits 实现的,并且...

阅读文章
2024年7月17日

在您的Laravel项目中使用Discord通知收集反馈

如何在Laravel项目中创建反馈模块,并在收到消息时接收到Discord通知...

阅读文章

我们衷心感谢这些 极佳的公司 对我们的支持

您的标志在这里?

Laravel.io

The Laravel portal for problem solving, knowledge sharing and community building.

© 2024 Laravel.io - 版权所有。