Site Banner Management System Development
A banner management system allows marketers to independently update promotional materials on the website — homepage sliders, catalog promo blocks, notification bars — without involving developers.
Data Model
banner_zones (
id, name, slug, description,
max_banners, -- limit number of active banners
aspect_ratio, -- '16:9', '4:1', etc. for upload hints
recommended_size -- '1920x400 px'
)
banners (
id, zone_id, title, subtitle,
desktop_image_url, mobile_image_url,
url, target: _self | _blank,
button_text, button_color,
order, is_active,
starts_at, ends_at, -- display schedule
created_by, created_at, updated_at
)
Display Schedule
Banner is shown only during specified period:
class Banner extends Model
{
public function scopeActive(Builder $query): Builder
{
return $query
->where('is_active', true)
->where(fn($q) => $q
->whereNull('starts_at')
->orWhere('starts_at', '<=', now())
)
->where(fn($q) => $q
->whereNull('ends_at')
->orWhere('ends_at', '>=', now())
);
}
}
API for Frontend
// GET /api/banners/{zone_slug}
public function index(string $zoneSlug): JsonResponse
{
$banners = Cache::remember("banners:{$zoneSlug}", 300, function () use ($zoneSlug) {
return Banner::whereHas('zone', fn($q) => $q->where('slug', $zoneSlug))
->active()
->orderBy('order')
->get(['id', 'title', 'subtitle', 'desktop_image_url', 'mobile_image_url', 'url', 'button_text']);
});
return response()->json($banners);
}
React Slider Component
function HeroBanner({ zoneSlug }) {
const { data: banners } = useQuery(['banners', zoneSlug], () =>
fetch(`/api/banners/${zoneSlug}`).then(r => r.json())
);
if (!banners?.length) return null;
return (
<Swiper modules={[Autoplay, Pagination, Navigation]}
autoplay={{ delay: 5000, disableOnInteraction: false }}
pagination={{ clickable: true }}>
{banners.map(banner => (
<SwiperSlide key={banner.id}>
<a href={banner.url}>
<picture>
<source media="(max-width: 768px)" srcSet={banner.mobile_image_url} />
<img src={banner.desktop_image_url} alt={banner.title} />
</picture>
</a>
</SwiperSlide>
))}
</Swiper>
);
}
Banner Analytics
Track impressions and clicks:
// On display (server-side rendering)
BannerImpression::create(['banner_id' => $banner->id, 'date' => today()]);
// On click (webhook or redirect)
Route::get('/banner-click/{banner}', function (Banner $banner, Request $request) {
BannerClick::create(['banner_id' => $banner->id, 'ip' => $request->ip()]);
return redirect($banner->url);
});
CTR = clicks / impressions — key metric for evaluating banner effectiveness.
Development timeline: 2–3 days for system with schedule, responsive images, and basic analytics.







