Skip to main content
Version: 1.x

← Previous · Back to README · Next →

Extending functionality

Conditional clauses​

note

Conditional clauses are available for Feed, FeedInfo and FeedItem classes.

Sometimes, you may want certain feed clauses to apply to a feed based on another condition. For instance, you may only want to apply a where statement if a given input value is present or another attribute has a value. You can accomplish this using the when method:

use DragonCode\LaravelFeed\Feeds\Feed;
use DragonCode\LaravelFeed\Feeds\Items\FeedItem;
use Illuminate\Database\Eloquent\Model;

class ProductFeed extends Feed
{
public function item(Model $model): FeedItem
{
return (new ProductFeedItem($model))
->when($model->category, function (ProductFeedItem $item) {
$item->title .= ' (first)';
});
}
}

The when method only executes the given closure when the first argument is true. If the first argument is false, the closure will not be executed. So, in the example above, the closure given to the when method will only be invoked if the category field is present on the model and evaluates to true.

You may pass another closure as the third argument to the when method. This closure will only execute if the first argument evaluates to false. To illustrate how this feature may be used, we will use it to configure the model title suffix:

use DragonCode\LaravelFeed\Feeds\Feed;
use DragonCode\LaravelFeed\Feeds\Items\FeedItem;
use Illuminate\Database\Eloquent\Model;

class ProductFeed extends Feed
{
public function item(Model $model): FeedItem
{
return (new ProductFeedItem($model))
->when(
value : $model->category,
callback: function (ProductFeedItem $item) {
$item->title .= ' (first)';
},
default : function (ProductFeedItem $item) {
$item->title .= ' (second)';
},
);
}
}

Additionally, the inverse method unless is available. In this case, the processing logic is the exact opposite.

use DragonCode\LaravelFeed\Feeds\Feed;
use DragonCode\LaravelFeed\Feeds\Items\FeedItem;
use Illuminate\Database\Eloquent\Model;

class ProductFeed extends Feed
{
public function item(Model $model): FeedItem
{
return (new ProductFeedItem($model))
->unless(
value : $model->category,
callback: function (ProductFeedItem $item) {
$item->title .= ' (second)';
},
default : function (ProductFeedItem $item) {
$item->title .= ' (first)';
},
);
}
}

Macroable​

Feed, FeedItem, and FeedInfo are "macroable", which allows you to add additional methods to them at runtime. The macro method accepts a closure that will be executed when your macro is called. The macro closure may access the feed's other methods via $this, just as if it were a real method of the feed classes.

For example, the following code adds a customFilename method to the Feed class:

use DragonCode\LaravelFeed\Feeds\Feed;
use Illuminate\Support\ServiceProvider;

class FeedServiceProvider extends ServiceProvider
{
public function boot(): void
{
Feed::macro('customFilename', function (Feed $feed, string $name) {
$this->filename = $name;

return $this;
});
}
}

Additionally, here are examples for FeedInfo and FeedItem:

use DragonCode\LaravelFeed\Feeds\Info\FeedInfo;
use Illuminate\Support\ServiceProvider;

class FeedServiceProvider extends ServiceProvider
{
public function boot(): void
{
FeedInfo::macro('titleWithPrefix', function () {
return sprintf('[%s]: %s', date('Y'), $this->title);
});
}
}

class ProductFeedInfo extends FeedInfo
{
public function __construct(
protected string $title,
) {}

public function toArray(): array
{
return [
'title' => $this->titleWithPrefix(),
];
}
}
use DragonCode\LaravelFeed\Feeds\Items\FeedItem;
use Illuminate\Support\ServiceProvider;

class FeedServiceProvider extends ServiceProvider
{
public function boot(): void
{
FeedItem::macro('titleWithPrefix', function () {
return sprintf('[%s]: %s]', $this->model->getKey(), $this->model->title);
});
}
}

class ProductFeedItem extends FeedItem
{
public function toArray(): array
{
return [
'title' => $this->titleWithPrefix(),
];
}
}

Custom transformers​

A transformer converts a supported value before the converter serializes it. Register transformer class names in config/feeds.php. Laravel resolves each class through the service container, so constructor dependencies and custom container bindings are supported.

namespace App\Feeds\Transformers;

use App\Services\PriceFormatter;
use DragonCode\LaravelFeed\Contracts\Transformer;

class PriceTransformer implements Transformer
{
public function __construct(
protected PriceFormatter $formatter,
) {}

public function allow(mixed $value): bool
{
return is_float($value);
}

public function transform(mixed $value): string
{
return $this->formatter->format($value);
}
}

Add the transformer to the configured list after the built-in transformers:

<?php

declare(strict_types=1);

use App\Feeds\Transformers\PriceTransformer;
use DragonCode\LaravelFeed\Transformers;

return [
'transformers' => [
Transformers\BoolTransformer::class,
Transformers\DateTimeTransformer::class,
Transformers\EnumTransformer::class,
PriceTransformer::class,
],
];

The configured transformers run in declaration order. Each allow() call receives the current value, including changes made by earlier transformers, and every matching transformer may update it. Duplicate entries are removed after container resolution while the first occurrence keeps its position.

A converter builds this ordered pipeline once and reuses the same transformer instances for every field in that feed generation.

Prepending feed transformers​

Override transformers() on a feed to prepend transformers for that feed. The method returns transformer class names, and Laravel resolves them through the service container in the same way as the global list.

For example, define a date transformer with a feed-specific format:

namespace App\Feeds\Transformers;

use DragonCode\LaravelFeed\Transformers\DateTimeTransformer;

class FeedDateTimeTransformer extends DateTimeTransformer
{
protected function format(): string
{
return 'Y-m-d';
}
}

Then return it from the feed:

namespace App\Feeds;

use App\Feeds\Transformers\FeedDateTimeTransformer;
use App\Models\Product;
use DragonCode\LaravelFeed\Feeds\Feed;
use Illuminate\Database\Eloquent\Builder;

class ProductFeed extends Feed
{
public function builder(): Builder
{
return Product::query();
}

public function transformers(): array
{
return [
FeedDateTimeTransformer::class,
];
}
}

The prepended list does not replace the global list from config/feeds.php. Transformers run in this order:

  1. Prepended transformers returned by transformers().
  2. Global transformers from config/feeds.php.
  3. Converter-specific transformers required by the selected output format.

A prepended transformer can therefore format a date first. The global date transformer receives the resulting string and skips it, while other global transformers still process matching fields.

See Also​