Lightweight & extensible OpenAds for Laravel, suitable for internal ads, marketplaces, CMS, or monetization platforms. All metrics (CTR, relevance, landing score) and click/view costs are automatically calculated based on ad performance.
π Search Ads (keyword-based)
πΌοΈ Image & π₯ Video Ads (URL / local / CDN)
π― Campaign β Ad Group β Ads β Keywords β Assets
β° Display based on time (start_time & end_time, default 24 hours)
π Target locations: country / city (multi, default all)
π± Target devices: android / ios / desktop (multi, default all)
π Auction & ranking (bid Γ quality score)
π Automatic impression & click tracking
π° Click cost = bid, view cost = bid Γ 20% (configurable)
π CTR, relevance, landing score & campaign balance calculated automatically
composer require lazyexe/openads
Or Manual / Local Development
"require": {
"lazyexe/openads": "*"
},
"repositories": [
{
"type": "path",
"url": "OpenAds"
}
],
Publish config & migrations:
php artisan vendor:publish --tag=ads-config
php artisan vendor:publish --tag=ads-migrations
php artisan migrate
<?php
use Illuminate \Support \Facades \Route ;
use App \Http \Controllers \HomeController ;
use App \Http \Controllers \AdsClickController ;
Route::get ('/ ' , [HomeController::class, 'index ' ]);
Route::get ('/ads/click/{adId} ' , [AdsClickController::class, 'click ' ])->name ('ads.click ' );
<?php
namespace App \Http \Controllers ;
use Illuminate \Http \Request ;
use OpenAds \Facades \Ads ;
class HomeController extends Controller
{
public function index (Request $ request )
{
$ query = $ request ->get ('q ' , '' );
$ adsCollection = $ query ? Ads::search ($ query )->limit (3 ) : collect ();
return view ('index ' , [
'ads ' => $ adsCollection ,
'query ' => $ query ,
]);
}
}
<?php
namespace App \Http \Controllers ;
use Illuminate \Http \Request ;
use OpenAds \Facades \Ads ;
class AdsClickController extends Controller
{
public function click (Request $ request , int $ adId )
{
$ url = Ads::logClick ($ adId );
if (!$ url ) {
abort (404 , 'Ads not found or insufficient budget. ' );
}
return redirect ()->away ($ url );
}
}
@if ($ads -> all () )
@foreach ($ads -> all () as $ad )
<div >
<div >Sponsored</div >
<h4 >{{ $ad -> title } } </h4 >
@if (! empty ($ad -> assets ) )
@php $asset = $ad -> assets [0 ]; @endphp
@if ($asset -> type === ' image' )
<img src =" {{ $asset -> source } }" alt =" " >
@elseif ($asset -> type === ' video' )
<video controls width =" 300" >
<source src =" {{ $asset -> source } }" >
</video >
@endif
@endif
<p >
<a href =" {{ route (' ads.click' , $ad -> id ) } }" target =" _blank" >
{{ $ad -> url } }
</a >
</p >
</div >
@endforeach
@else
<p >No ads available.</p >
@endif
Method
Description
Return
Ads::search(string $query)
Retrieve ads by keyword query, automatically logs impressions & charges views
AdCollection
AdCollection->all()
Get all ads
array of AdDTO
AdCollection->limit(int $n)
Get top n ads by score
AdCollection
Ads::logClick(int $adId)
Log click & charge campaign according to bid
`string
null` (landing URL)
config/ads.php
return [
'default_platform ' => 'search ' ,
'view_cost_percent ' => 0.2 , // view cost = bid * 20%
];
Ads only display if campaign & ad group are active, budget > 0, and within date range (start_date & end_date).
CTR, relevance, and landing score are calculated automatically from impressions & clicks when a query runs.
All costs and performance updates occur immediately at query time, keeping data dynamic and accurate.
ποΈ Database Structure
Table
Purpose
ads_campaigns
Store campaigns & budgets
ads_ad_groups
Group keywords & ads
ads_ads
Ad unit (text / image / video)
ads_keywords
Keyword targeting
ads_assets
Ad assets (image / video)
ads_impressions
Ad impression log
ads_clicks
Ad click log
Field
Description
name
Campaign name
daily_budget
Daily budget
status
active / paused
start_date
Campaign start date
end_date
Campaign end date
start_time
Start time (nullable = runs 24 hours)
end_time
End time (nullable = runs 24 hours)
target_locations
JSON: target countries/cities, null = all locations
target_devices
JSON: target devices (android/ios/desktop), null = all devices
Field
Description
campaign_id
Related campaign
name
Ad group name
default_bid
Default bid
status
active / paused
Field
Description
ad_group_id
Related ad group
title
Ad title
url
Landing page URL
bid
Bid price
ctr
Click-through rate
relevance
Relevance score
landing_score
Landing score
status
active / paused
βΉοΈ Ads can be text-only or include image / video assets .
Field
Description
ad_group_id
Related ad group
keyword
Target keyword
match_type
exact / phrase / broad
negative
Exclude keyword
Field
Description
ad_id
Related ad
type
image / video
source
URL or local path
is_primary
Primary asset
Source can be:
CDN / external URL
Local upload (storage/ads/...)
S3 / object storage