Each placement has its own single-file creative bucket with a display variant fit (cover) to that placement’s width × height. Attach an image and read it back, or render it — an image when one exists, the text ad otherwise:
// Attach (or replace) the creative for a placement — model, id, or slug.
$ad->addCreative($request->file('banner'), 'sidebar'); // an UploadedFile or a file path
$ad->creativeFor('sidebar'); // ?Media (placement-specific, then generic)
$ad->creativeUrl('sidebar'); // display-variant URL, or '' / fallback when empty
$ad->creativeUrl('sidebar', ''); // the original
// Responsive <img> when a creative exists, else the text-ad fallback (name + description + price).
echo Advertisements::render($ad, 'sidebar', ['class' => 'ad']);
echo $ad->renderCreative('sidebar', ['class' => 'ad']); // the same, from the modelBuckets
Buckets are named {creative_bucket_prefix}:{placement-slug}, plus a generic, size-less {fallback_bucket}. Each accepts JPEG, PNG, WebP, GIF and AVIF, keeps a single file (a new upload replaces the old one) and uses media.disk and media.responsive_widths when set. A placement without dimensions gets a display variant with no resize.
$ad->creativeBucketName('sidebar'); // "creative:sidebar"
$ad->fallbackCreativeBucket(); // "creative"
$ad->displayVariant(); // "display"
// A generic, size-less creative for any placement without its own image:
$ad->addMedia($request->file('banner'))->toMediaBucket($ad->fallbackCreativeBucket());- creativeFor() — the placement-specific image, then the generic creative (when media.use_fallback_bucket is on), else null.
- creativeUrl() — the display-variant URL by default; pass another variant name, or '' for the original. Returns the bucket’s fallback URL or '' when nothing is attached.
- Advertisements::render($ad, $placement) — a responsive <img> with srcset (alt defaults to the ad’s name) when an image creative exists, otherwise the text ad. $ad->renderCreative() is sugar for the same call.
Text-ad fallback
Without an image creative, render() renders the advertisements::text-ad Blade view: a div.advertisement-text-ad sized to the placement’s dimensions, with a data-advertisement attribute, holding the name, the description when filled and the price formatted in the active locale. Publish the view to restyle it, or point media.text_ad_view at your own view — a view that doesn’t exist fails loudly with an InvalidArgumentException.
php artisan vendor:publish --tag="advertisements-views"
# → resources/views/vendor/advertisements/text-ad.blade.php- $advertisement — the Advertisement being rendered.
- $placement — the resolved ?Placement.
- $attributes — the array passed to render() or renderCreative().
Render attributes
The text ad applies every attribute you pass to its <div>, escaped: class and style are appended to its own (class="advertisement-text-ad ad"), anything else is added or overrides. The image path hands them to media-library’s responsive <img>, which takes class, alt (defaulting to the ad’s name) and sizes.
Custom renderer
Advertisements::render() goes through the bound RoundlyConsulting\Advertisements\Contracts\CreativeRenderer. Bind your own to swap the whole resolution chain:
use Illuminate\Support\HtmlString;
use RoundlyConsulting\Advertisements\Contracts\CreativeRenderer;
use RoundlyConsulting\Advertisements\Models\Advertisement;
use RoundlyConsulting\Advertisements\Models\Placement;
final class NativeAdRenderer implements CreativeRenderer
{
public function render(Advertisement $advertisement, Placement|int|string $placement, array $attributes = []): HtmlString
{
return new HtmlString(view('ads.native', ['ad' => $advertisement])->render());
}
}
// AppServiceProvider::register()
$this->app->bind(CreativeRenderer::class, NativeAdRenderer::class);Creatives are media-library Media rows, so everything that package offers — variants, responsive srcset, private and temporary URLs — applies to them.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.