DEV Community

Richard Fu for RF Game Dev

Posted on • Originally published at richardfu.net on

Develop House-Ads API With Laravel For Mobile App/Game

You’re probably familiar with house ads (or cross-promotion ads) in mobile games. Famous game publishers like Vodoo use house ads extensively to cross-promo their products. While you can put the house ads locally and pre-built them in the apps, having a server to serve the API and assets can give you live control of the ads. You can also use this as app event notices, to gives dynamic information and interaction with your users.

This article will walk you through how to create the API calls with Laravel step-by-step.

TLDR; You can use review the code or simply install my Laravel House Ads package.

Overview

We are aiming to design a generic API, that allows clients to serve the house ads in different formats with the corresponding analytic data. Two most use cases are:

  1. UI Box
  2. Interstitial Popup

UI Box

In house ad in UI box, Voodoo

The most used house ads type that is used by most game publishers, usually a GIF/video playing in the menu and do not interfere with the users. For analytics, we may be interested in how many times an ad shows and how many times it has clicked.

Interstitial Popup

A popup that usually shows in app-launch. It may contain a cancel button to go back to the app, and a confirm button to link to the cross-promo app or webpage. The interstitial popup can also be used as an in-app notification for anything you want to inform the users, such as version-upgrade, offline events, etc. For analytics, we are interested in how many times an ad shows, cancel and confirm button are clicked.

Database migrations

By using Laravel’s database migrations, the database setup is pretty simple, we only need 1 database:

House Ads Table




php artisan make:migration create_house_ads_table



Enter fullscreen mode Exit fullscreen mode

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




<?php

use IlluminateDatabaseSchemaBlueprint;
use IlluminateDatabaseMigrationsMigration;
use IlluminateSupportFacadesSchema;

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

            $table->integer('game_id')->unsigned();
            $table->string('media_portrait', 128)->nullable();
            $table->string('media_landscape', 128)->nullable();
            $table->boolean('open_url')->default(true);
            $table->string('url_ios', 256)->nullable();
            $table->string('url_android', 256)->nullable();
            $table->tinyInteger('repeat_count')->unsigned()->default('1');
            $table->tinyInteger('priority')->unsigned()->default('1');
            $table->date('start_at');
            $table->date('end_at');
            $table->mediumInteger('shown_count')->unsigned()->default('0');
            $table->mediumInteger('confirmed_count')->unsigned()->default('0');
            $table->mediumInteger('cancelled_count')->unsigned()->default('0');

            $table->timestamps();

            $table->foreign('game_id')->references('id')->on('games')->onDelete('cascade');
        });
    }

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



Enter fullscreen mode Exit fullscreen mode
  • game_id – which game/app this house ad redirect to. This is used in client app to distingish and not showing its own house ad.
  • media_portrait – the portrait image/video filename. The media should be uploaded and stored in <server root>/media folder. You can also use video and implement the playing function in client app.
  • media_landscape – the landscape image/video filename.
  • open_url – should the house ad open and URL, otherwise the client should simply show the image/video. This is mostly used for showing an event image, cutscene video, etc.
  • url_ios – the URL to open in iOS devices.
  • _url__android – the URL to open in Android devices.
  • repeat_count – how many app launches to wait to show this ad again, for interstitial popup only.
  • priority – the highest priority ad get shown in one app launch.
  • start_at – the date that this house ad start, used this to schedule future house ads.
  • end_at – the date that this house ad end, used this to end promotion with a given period.
  • shown_count – the shown count, used for analytics only.
  • confirmed_count – the confirmed count (successful redirect), used for analytics only.
  • cancelled_count – The cancelled count (failed redirect), used for analytics only.

Note that we use the Game Essentials package here for basic game database setup, which will not be covered in this article.

Models

Then, we simply map the database table as its own Laravel Eloquent model. Again, we only need one model here:

House Ad model




php artisan make:model HouseAd



Enter fullscreen mode Exit fullscreen mode

/app/Models/HouseAd.php:




<?php

namespace FuricHouseAdsModels;

use IlluminateDatabaseEloquentModel;
use FuricGameEssentialsModelsGame;

class HouseAd extends Model
{

    protected $guarded = [];

    protected $hidden = ['game_id', 'media_portrait', 'media_landscape', 'confirmed_count', 'cancelled_count', 'start_at', 'end_at', 'created_at', 'updated_at'];

    protected $appends = ['url_media_portrait', 'url_media_landscape', 'game'];

    public function getUrlMediaPortraitAttribute()
    {
        return url('/media/'.$this->media_portrait);
    }

    public function getUrlMediaLandscapeAttribute()
    {
        return url('/media/'.$this->media_landscape);
    }

    public function game()
    {
        return $this->belongsTo(Game::class);
    }

    public function getGameAttribute()
    {
        return $this->game()->first();
    }

}



Enter fullscreen mode Exit fullscreen mode

HouseAd has belongsTo relationship to a game model, and contains the addictive media URL fields.

Routing

We simply have the API route in Laravel Routing. You may want to add Web route so for the admin console to manage the house ads, but again not covered here.

API route

In /routes/api.php, add:




<?php

use IlluminateSupportFacadesRoute;
use FuricHouseAdsHttpControllersHouseAdController;

Route::prefix('api')->group(function() {

    Route::resource('house-ads', HouseAdController::class)->only([
        'index', 'show', 'update'
    ]);

});



Enter fullscreen mode Exit fullscreen mode

This creates API routes as {api-url}/house-ads and {api-url}/house-ads/{id} for your client app to checking the current house ads and update specific one for analytics.

Controllers

Finally, we will have one Laravel Controller:

HouseAdController




php artisan make:controller HouseAdController



Enter fullscreen mode Exit fullscreen mode

/Http/Controllers/HouseAdController.php:




<?php

namespace FuricHouseAdsHttpControllers;

use FuricHouseAdsModelsHouseAd;
use AppHttpControllersController;
use IlluminateHttpRequest;
use Validator;

class HouseAdController extends Controller
{

    /**
     * Display a listing of the house ad resource.
     *
     * @return IlluminateHttpResponse
     */
    public function index()
    {
        return response(HouseAd::whereDate('start_at', '<=', date('Y-m-d'))->whereDate('end_at', '>=', date('Y-m-d'))->orderBy('priority', 'desc')->get(), 200);
    }

    /**
     * Display the specified house ad resource.
     *
     * @param int $id
     * @return IlluminateHttpResponse
     */
    public function show($id)
    {
        try {
            return response(HouseAd::findOrFail($id), 200);
        } catch (IlluminateDatabaseEloquentModelNotFoundException $e) {
            return response([
                'error' => 'No house ad found.'
            ], 400);
        }
    }

    /**
     * Update the specified house ad resource in storage.
     *
     * @param Request $request
     * @param int $id
     * @return IlluminateHttpResponse
     */
    public function update(Request $request, $id)
    {
        $validator = Validator::make($request->all(), [
            'confirmed' => 'sometimes|required|numeric',
            'shown' => 'sometimes|required|numeric',
        ]);
        if ($validator->fails()) {
            return response([
                'error' => 'Key "confirmed" required.'
            ], 400);
        }

        try {
            $houseAd = HouseAd::findOrFail($id);
            if ($request->has('confirmed')) {
                if ($request->confirmed == '1') {
                    $houseAd->confirmed_count++;
                } else {
                    $houseAd->cancelled_count++;
                }
            }
            if ($request->has('shown')) {
                $houseAd->shown_count++;
            }
            $houseAd->save();
            return response($houseAd, 200);
        } catch (IlluminateDatabaseEloquentModelNotFoundException $e) {
            return response([
                'error' => 'No house ad found.'
            ], 400);
        }
    }

}



Enter fullscreen mode Exit fullscreen mode

In HouseAdConstroller, we only index(), show() and update().

  • index() – listing the house ads which current datetime is within their start_at and end_at.
  • show() – showing a specific house ad, for debug only.
  • update() – updating the analytics count of a given house ad.

Conclusion

That’s it! We’ve set up a simple API call to list and update house ads. Now simply add the database entries and their corresponding media file in <server root>/media folder, it should be ready to go!

Again, you can read all code in the GitHub repo or simply install the package using composer, 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 in Unity later. 😀

Lastly, leave a comment if you got any questions and wish this article and the package may help you.

The post Develop House-Ads API With Laravel For Mobile App/Game appeared first on Richard Fu.

Top comments (0)