Develop Redeem Code API With Laravel For Mobile App/Game
Photo by Lewis Kang'ethe Ngugi on Unsplash
Have you ever wanted to add a redeem code feature to your game or app? A redeem code system is always useful for rewarding your users and boost user retention in events, or compensate the angry users for any particular bugs. While there are quite a few free or paid third-party solutions on the market, but making your own gives you more control and customized features as you would like. This article will walk you through how to create API calls and simple console pages with Laravel step-by-step.
TLDR; You can use review the code or simply install my Laravel Redeem Codepackage.
Database migrations
By using Laravel's database migrations, we need 4 databases as below:</p>
Events table
First, we create an events
table, each event can have multiple redeem codes. Few event examples are: 1-st anniversary giveaway, login bug compensation, etc.
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 - the event type, e.g. 1 is marketing event, 2 is player compensation, etc.
- name - the name of the event, for internal console display only.
- start_at, end_at - used to redeem codes only within the valid date.
Redeem Codes table
Next, we'll have the <code>redeem_codes</code> table, of course:
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 - we've set the redeem code fixed length of 12.
- reusable - a redeem code can be reusable and can be used by multiple users, default no.
- redeemed - set the true if a user redeemed the code. A reusable code will never be set to redeemed.
Redeem Code Rewards table
Then the redeem_codes
table is for the rewards of the 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 - the reward type in integer, e.g. can be coins, gems or whatever in your app.
- amount - the reward amount.
- item_id - optional, used for giving a specific item, such as weapon ID.
Redeem Code Histories table
Last, we have a redeem_code_histories
table. It's optional for keeping track the redeeming records:
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');
}
}
Models
Then, we simply map each database table as its own Laravel Eloquent model:
Event model
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
has hasMany
relationship to both RedeemCode
and RedeemCodeReward
models.
RedeemCode model
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
has belongsTo
relationship to Event
model, and hasMany
relationship to RedeemCodeReward
model. Note that a redeem code can be created without an event, and simply contains its redeem rewards.
RedeemCodeReward model
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
model simply has belongsTo
relationship to both RedeemCode
and Event
models.
RedeemCodeHistory model
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');
}
}
Lastly, RedeemCodeHistory
model belongsTo
RedeemCode
model.
Routing
We simply has two routes in Laravel Routing, one for API and one for our admin console web.
API route
In /routes/api.php, add:
<?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');
});
This creates an API route as {api-url}/redeem/{code} for your client app to access when redeeming the code.
Web route
In /routes/web.php, add:
<?php
use Illuminate\Support\Facades\Route;
use Furic\RedeemCodes\Http\Controllers\RedeemCodeController;
Route::resource('redeem-codes', RedeemCodeController::class);
This creates a web route as {url}/redeem-codes
for you to access the redeem console page, to create/edit/delete redeem codes there. Note that we don't have authorization here and anyone can access the page. You can always add the authorization middleware here, but this wouldn't be covered by this article which aims to demonstrate in the simplest way.
Controllers
Moving to the most important and logic part, we will have two Laravel Controllers, one for API and one for web just like routing:
RedeemController
/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);
}
}
In RedeemConstroller
, we only have one function to check and validate the inputted redeem code:
- First, check if the redeem code exists.
- Check if the redeem code previously redeemed.
- Check if the redeem code belongs to an event, if yes then check the event valid date range.
- Set the code as redeemed, if not a reusable code.
- Create a redeem history record.
RedeemCodeController
/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
is more complicated since it contains all redeem code listing, create, edit and delete functions here. Most functions are self-descriptive, we will just go through the create
function here:
- Takes an input of integer:
count
. - Creates a new
Event
entry with the givenname
. - Creates a number of
RedeemCode
entries with thecount
value. - Creates a number of
RedeemCodeReward
entries with the number of the givenreward_types
array.
Views
Finally, we need Laravel Views pages for our web console. Again, for simplicity, we only have 3 pages here: index, edit and added.
Layout blade page
/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.bootstrapcdn.com/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.bootstrapcdn.com/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>
This is simply a style layout page with Bootstrap and Font Awesome, you can use your own style if you wish.
Index blade page
/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>&nbsp;</th>
<th>&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
The web console Index page of Redeem Code.
The index page is a bit long, bascially it does the following:
- A new redeem code form, it will send a
POST
request and run thecreate()
in RedeemCodeController.php. - List all redeem codes, with the links to edit, reset and delete.
Added blade page
/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
This code simply shows a confirm page after adding redeem codes and lists all newly generated codes so you can send them to your users.
Edit blade page
/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
This page shows the loaded redeem code and contains a form to update the code. it will send a PUT
request and run the update()
in RedeemCodeController.php.
Conclusion
That's it! Although it seems to require quite a few scripts to implement, most of the code follow Laravel MVC architecture and makes things tidier and much easier to maintain in long run.
Again, you can read all code in the GitHub repo, while the project is still pretty simple and there's few TODOs.
Oh, that's the server back-end part, I will write another post on how to call the API and handle the response later. 😀
Lastly, leave a comment if you got any questions and wish this article and the package may help you.
haneef5k, blazodragan, furic, ilasisi, sam-app liked this article