General

Requests

Requests

What are the available requests?

Storefront

Listed below are all the storefront validation requests:

Admin

Listed below are all the admin validation requests:

General

Catalog Management

Content Management

Customer Management

Discount Management

Order Management

Reports

Two Factor Authentication

Configuration

Checkout

Listed below are all the checkout validation requests:

Account Area

Listed below are all the account area validation requests:

Requests

How can I extend an existing request?

The expects() method can be used to add custom validation rules (and messages) for your custom field (or to overwrite the existing rules/messages for an existing field):

// Adding a custom field
\Aero\Admin\Http\Requests\Catalog\CreateProductRequest::expects('acme', 'required|string|max:255', [
    'acme.required' => 'The acme field is required.',
]);

// Making existing categories field required
\Aero\Admin\Http\Requests\Catalog\CreateProductRequest::expects('categories', 'required|array');

Parameters

Parameter
Type
Description
$field
string
The key of the field to validate.
$rules
string|array|\Closure
The rules to use when validating. If callback is supplied, it will receive the request as the argument.
$messages
array|null
The messages for the rules, key's are of the form {$field}.{$rule}.

Pipelines

Pipelines

What are the available pipelines?

Pipeline
Description
\Aero\Store\Pipelines\ContentForBody Builds content string to be injected into the body of all pages.
\Aero\Store\Pipelines\ContentForHead Builds content string to be injected into the head of all pages.
\Aero\Store\Pipelines\RobotsTxt Used to defined/generate a robots.txt file.
\Aero\Store\Pipelines\CartItemBuilder Run whenever a cart item is added to the cart in order to build the item from the request data.
\Aero\Cart\Pipelines\OrderItemBuilder Run whenever a cart item is transformed into an order item.
\Aero\Subscription\Pipelines\SubscriptionOrderBuilder Run when a subscription is processed and requires a new order to be created.
\Aero\Admin\Pipelines\ListingRulesContextBuilder
Builds listing rule context for each rule type.
\Aero\Admin\Pipelines\ListingRulesDataFormatter Formats listing rules for admin UI.
\Aero\Admin\Pipelines\PreviewMailAction
Builds a temporary mail action to be previewed.
\Aero\Admin\Reports\Pipelines\BuyXDiscountTierReportQuery Builds the query for the buy x discount tier report.
\Aero\Admin\Reports\Pipelines\DiscountCodesReportQuery Builds the query for the discount codes report.
\Aero\Admin\Reports\Pipelines\OrderBreakdownReportQuery Builds the query for the order breakdown report.
\Aero\Admin\Reports\Pipelines\OrderSurchargeReportQuery Builds the query for the order surcharge report.
\Aero\Admin\Reports\Pipelines\ProductStockLevelReportQuery Builds the query for the product stock level report.
\Aero\Admin\Reports\Pipelines\RefundReportQuery Builds the query for the refund report.
\Aero\Admin\Reports\Pipelines\SalesReportQuery Builds the query for the sales report.
\Aero\Admin\Reports\Pipelines\VoucherReportQuery Builds the query for the voucher report.
Aero\Admin\Configuration\Groups\PaymentAndShippingConfigurationGroup Builds the payment and shipping configuration group.
Aero\Admin\Configuration\Groups\OrderManagementConfigurationGroup Builds the order management configuration group.
Aero\Admin\Configuration\Groups\LocalizationConfigurationGroup Builds the localization configuration group.
Aero\Admin\Configuration\Groups\ProductManagementConfigurationGroup Builds the product management configuration group.
Aero\Admin\Configuration\Groups\NotificationsConfigurationGroup Builds the notifications configuration group.
Aero\Admin\Configuration\Groups\TaxConfigurationGroup Builds the tax configuration group.
Aero\Admin\Configuration\Groups\SystemConfigurationGroup Builds the system configuration group.
Aero\Admin\Configuration\Groups\UserManagementConfigurationGroup Builds the user management configuration group.
Aero\Admin\Configuration\Groups\MaintenanceConfigurationGroup Builds the maintenance configuration group.
Aero\DataPort\Commands\Pipelines\ImportBlockCSVPipeline Creates a block from CSV data.
Aero\DataPort\Commands\Pipelines\ImportProductCSVPipeline Creates a product from CSV data.
\Aero\Store\Pipelines\ListingsPageQuery Builds the query for the listings page.
Aero\Search\Elastic\Pipelines\ProductAttributes Builds the attributes for the ES Product model
Aero\Search\Elastic\Pipelines\ListingAttributes Builds the attributes for the ES Listing model
Aero\Search\Elastic\Pipelines\ListingCategory Builds listing documents categories data.
Aero\Search\Elastic\Pipelines\ListingCategoryFacet Builds listing documents category string facet data.
Aero\Search\Elastic\Pipelines\ListingManufacturerFacet Builds listing documents manufacturer string facet data.
Aero\Search\Elastic\Pipelines\ListingTagFacet Builds listing documents tag string facet data.
Aero\Search\Elastic\Pipelines\Mapping Used to build the ES Mapping for search documents.
Pipelines

How do I extend pipelines?

The payload data can be altered using the extend() method. This is typically done from within a Service Provider:

For example, if a module needs to add an option to items to flag them as requiring gift wrap, the CartItemBuilder pipeline class should be extended in the module's ServiceProvider as shown below:

<?php

namespace Aerocargo\Acme;

use Aero\Cart\CartItemOption;
use Aero\Common\Providers\ModuleServiceProvider;
use Aero\Store\Pipelines\CartItemBuilder;

class ServiceProvider extends ModuleServiceProvider
{
    public function setup() 
    {
        CartItemBuilder::extend(function ($item) {
            if (request()->has('gift_wrap')) {
                $option = CartItemOption::create('Gift wrap');

                $item->addOption($option);
            }
        });
    }
}

 

 

Response Builders

Response Builders

What are the available response builders?

Storefront

Listed below are all the storefront response builders, the vast majority are view page responses or cart action responses:

Admin

Listed below are all the admin response builders, the vast majority are create/edit page responses or store/update responses:

General

Dashboard

Catalog Management

Content Management

Customer Management

Discount Management

Order Management

Two Factor Authentication

Configuration

Checkout

Listed below are all the checkout response builders:

Account Area

Listed below are all the account area response builders:

Response Builders

How do I pass data to a response?

The most common task performed when extending responses, is setting additional data. Typically, for HTML responses that render from a view, this involves sending data to the view to be used as a variable. If the response returns JSON, it may be adding additional key/value pairs.

For example, if you needed to obtain customer reviews for a given product and output them on the product page, you could do the following, which passes a product to a 3rd party reviews API and then adds the reviews it returns to the response data, which is then passed to the product.twig view file ready to be loop around and outputted:

\Aero\Store\Http\Responses\ProductPage::extend(function (\Aero\Store\Http\Responses\ProductPage $page, \Closure $next) {
    $reviews = ReviewsAPI::forProduct($page->product);

    $page->setData('reviews', $reviews);

    return $next($page);
});

Another example is getting the latest 5 blog posts from a blog module and sending them to the homepage to be displayed:

\Aero\Store\Http\Responses\Homepage::extend(function (\Aero\Store\Http\Responses\ProductPage $page, \Closure $next) {
    $posts = \Acme\BlogPosts::latest()->limit(5)->get();

    $page->setData('posts', $posts);

    return $next($page);
});

You can obtain the existing data set against the response builder using the getData() method.

You can remove existing data set against the response builder using the removeData() method.

Response Builders

How do I redirect a response?

Sometimes you may wish to redirect the user to another location, either to an external site or within the store. To do so, pass the URL, route(), or an Illuminate\Http\RedirectResponse instance to the setRedirect() method.

For example, if you stored the age of customers (computed from their provided date of birth), and needed to prevent access to those under the age of 18:

\Aero\Store\Http\Responses\ProductPage::extend(function (\Aero\Store\Http\Responses\ProductPage $page, \Closure $next) {
    if ($page->request->customer()->age < 18) {
        $page->setRedirect('/denied');
    }

    return $next($page);
});

As extension code is evaluated in the order it's added to the response builder, code that runs after this step may override the redirect.

To force a redirect and prevent execution of the code that follows in the pipeline, you should return an instance of the redirect() helper:

\Aero\Store\Http\Responses\ProductPage::extend(function (\Aero\Store\Http\Responses\ProductPage $page, \Closure $next) {
    if (! $page->product->canBeViewedByIp($page->request->ip())) {
        return redirect('/denied');
    }

    return $next($page);
});
Response Builders

How do I set the response status code?

There may be situations where the HTTP response status needs to be altered. By default, the returned status is 200, however, this can be changed by passing the new status code to the setStatus() method:

\Aero\Store\Http\Responses\ProductPage::extend(function (\Aero\Store\Http\Responses\ProductPage $page, \Closure $next) {
    $page->setStatus(403);

    return $next($page);
});


Response Builders

How do I set response headers?

When extending the response builder, you can set HTTP headers to be sent with the response:

\Aero\Store\Http\Responses\ProductPage::extend(function ($page) {
    $page->setHeader('foo', 'bar');
});

 

Response Builders

How do I attach middleware to a response builder?

Since Aero does not expose the underlying routes and controllers, middleware can be attached directly to the response builder. This allows for code to be executed before the main pipeline is run.

For example, to restrict all products from being accessible to guest visitors, you could register the auth middleware to the ProductPage response:

\Aero\Store\Http\Responses\ProductPage::middleware('auth');

When assigning middleware, you can also pass the fully qualified class name:

\Aero\Store\Http\Responses\ProductPage::middleware(\App\Http\Middleware\CheckAge::class);

Alternatively, you can provide a Closure.

For example, middleware could be added to the homepage to set a tracking cookie from the referrer:

\Aero\Store\Http\Responses\Homepage::middleware(function (\Illuminate\Http\Request $request, \Closure $next) {
    return tap($next($request), function ($response) use ($request) {
        $response->cookie('referer', $request->header('Referer'));
    });
});

Refer to the Laravel documentation for more information on middleware.

Response Builders

How do I change the response view?

For responses that return HTML, a Twig view file is used. Whilst these are originally defined, the view can be changed. For example, to change the product page view based on product data:

\Aero\Store\Http\Responses\ProductPage::extend(function (\Aero\Store\Http\Responses\ProductPage $page, \Closure $next) {
    if ($view = $page->product->additional('view')) {
        $page->setView($view);
    }

    return $next($page);
});

Setting the view to null will result in a JSON response.

You can get the current view for a response using getView().

Services

Services

What is the tax accountant service?

The Tax Account is the service responsible for determining the tax multiplier that should be applied to a product price.

It uses the store's tax configuration together with the current customer's tax information and location to determine which tax rules and rates apply. The service can then use those multipliers to convert prices between tax-exclusive and tax-inclusive values.

Converting prices

Ex => Inc

The priceInc method can be used to convert an ex price to an inc price, we use it when handling prices for products:

app('aero.accountant')->priceInc($value, $this->product_tax_group_id);

Inc => Ex

The priceEx method can be used to convert an inc price to an ex price, we use it when handling prices for products:

app('aero.accountant')->priceEx($value, $this->product_tax_group_id);

Resolvers

Tax Group Resolver

The Tax Account uses a tax group resolver to resolve a customer's tax group id.

A resolver is set by the Aero\Store\Http\Middleware\SetTaxResolver middleware:

$this->accountant->setTaxGroupResolver(static function (?\Aero\Account\Models\Customer $customer) {
    if ($customer && ($group = $customer->getAttribute('customer_tax_group_id'))) {
        return $group;
    }

    if ($customer && $customer->customer_group_id && ($group = $customer->group->getAttribute('customer_tax_group_id'))) {
        return $group;
    }

    return null;
});

If no customer tax group id is resolved, the store's default will be used.

Country Resolver

The Tax Account uses a country resolver to resolve the country it should use.

A resolver is set by the Aero\Store\Http\Middleware\SetTaxResolver middleware:

$this->accountant->setCountryResolver(static function (?\Aero\Account\Models\Customer $customer) {
    if ($cartLocale = \Aero\Cart\Facades\Cart::instance(PaymentDriver::MOTO)->locale()) {
        return $cartLocale->countryCode;
    }
    
    return null;
});

If no country code is resolved, the store's default will be used.

Zone Resolver

The Tax Account uses a country resolver to resolve the country it should use.

A resolver is set by the Aero\Store\Http\Middleware\SetTaxResolver middleware:

$this->accountant->setZoneResolver(static function (?\Aero\Account\Models\Customer $customer) {
    if ($cartLocale = \Aero\Cart\Facades\Cart::instance(PaymentDriver::MOTO)->locale()) {
        return $cartLocale->zoneId;
    }
    
    return null;
});

If no zone id is resolved, then no zone id will be used.

Postcode Resolver

The Tax Account uses a country resolver to resolve the country it should use.

A resolver is set by the Aero\Store\Http\Middleware\SetTaxResolver middleware:

$this->accountant->setPostcodeResolver(static function (?\Aero\Account\Models\Customer $customer) {
    if ($cartLocale = \Aero\Cart\Facades\Cart::instance(PaymentDriver::MOTO)->locale()) {
        return $cartLocale->postcode;
    }
    
    return null;
});

If no postcode is resolved, then no postcode will be used.

Services

What is the combination serializer service?

The combination serializer service is used to serialize and deserialize models.

Serializing

This is used to take a collection of models and convert them into a string that can be used to find a corresponding combination.

Let's say you have a category called mens, a manufacturer called acme and a blue tag in the group color. Then if you visit /mens/acme/blue on the storefront it will load any products in the category mens that have the manufacturer acme and are tagged with blue.

Perhaps you might rather that it 404s unless a combination for those models exists (as otherwise there may very likely be no products that matches the criteria). This can be done by serializing the slugs and considering them invalid if they don't correspond to an existing combination model:

\Aero\Store\Routing\Slugs::setValidator(function (\Illuminate\Database\Eloquent\Collection $slugs, $resolver) {
    if ($resolver !== 'listings') return true;

    try {
        $serialized = \Aero\Common\Services\CombinationSerializer::serialize(
            $slugs->map(function (\Aero\Store\Models\Slug $slug) {
                return $slug->model;
            })
        );

        return \Aero\Catalog\Models\Combination::where('serialized', $serialized)->exists();
    } catch (\Exception $e) {
        return false;
    }
});

Deserializing

This is used to take a combination model and find all of its' component models (collections, categories, manufacturers & tags).

For example, you might want to resolve the category ids for the listings page from the combination:

\Aero\Store\Http\Responses\ListingsPage::extend(function (\Aero\Store\Http\Responses\ListingsPage $page, \Closure $next) {
    $categoryIds = \Aero\Common\Services\CombinationSerializer::deserialize($page->getData('combination'))
        ->filter(function (\Illuminate\Database\Eloquent\Model $model) {
            return $model instanceof \Aero\Catalog\Models\Category;
        })
        ->pluck('id');

    // Do stuff...

    return $next($page);
});

Services

What is the login blocker service?

The login blocker service can be used to block customers from logging in based on criteria.

For example, you might want to blacklist certain IPs.

First, define a setting to store blacklisted IPs:

\Aero\Common\Facades\Settings::group('customers', static function (\Aero\Common\Settings\SettingGroup $group) {
    $group->array('blocked_ip_addresses');
});

Then add a login blocker to check if the customer IP is blocked:

\Aero\Common\Facades\LoginBlocker::add(function (\Aero\Account\Models\Customer $customer) {
    if (in_array(request()->ip(), setting('customers.blocked_ip_addresses')) {
        return __('Blocked due to suspicious activity');
    }  
});

Returning a string means that they customer will be blocked from logging in and that string will be displayed.

Services

What is the volume service?

The volume service is used to normalize volumes into the store's base unit (cm^3 by default) as well as convert between different units.

Units

By default the volume service has the following units:

Adding a Unit

Use addUnit() to add a unit, for example we can add millilitres & litres:

\Aero\Common\Facades\Volume::addUnit('ml', 'ml');

\Aero\Common\Facades\Volume::addUnit('l', 'L')
    ->setNormalizer(function ($value) {
        return $value * 1000;
    });

setNormalizer defines how to get from that unit to the base unit, which is cm^3.

Removing a Unit

\Aero\Common\Facades\Volume::removeUnit('m^3');

Converting between Units

You can use convert() to convert between any two units, or normalize() to convert into the store's base unit.

\Aero\Common\Facades\Volume::convert(1000, 'mm^3', 'cm^3');

\Aero\Common\Facades\Volume::normalize('mm^3', 1000);

Setting a base Unit

\Aero\Common\Facades\Volume::setBaseUnit('ml');

When you change base unit, database values will still remain in cm^3. But volumes will be displayed in the new unit on screens such as the fulfillment create page.

Services

What is the weight service

The weight service is used to normalize weights into the store's base unit (g by default) as well as convert between different units.

Units

By default the weight service has the following units:

Adding a Unit

Use addUnit() to add a unit, for example we can add milligrams:

\Aero\Common\Facades\Weight::addUnit('mg', 'mg')
    ->setNormalizer(function ($value) {
        return $value / 1000;
    });

setNormalizer defines how to get from that unit to the base unit, which is g.

Removing a Unit

\Aero\Common\Facades\Weight::removeUnit('kg');

Converting between Units

You can use convert() to convert between any two units, or normalize() to convert into the store's base unit.

\Aero\Common\Facades\Weight::convert(1000, 'oz', 'g');

\Aero\Common\Facades\Weight::normalize('oz', 1000);

Setting a base Unit

\Aero\Common\Facades\Weight::setBaseUnit('oz');

When you change base unit, database values will still remain in grams. But weights will be displayed in the new unit on screens such as the fulfillment create page.

Commands

Commands

What are the available commands?

php artisan make:module

This command scaffolds a module for you (you need to provide a valid name for the module such as aerocargo/my-module). This involves setting up composer and publishing some default module stubs.

php artisan aero:configure

This command walks you through configuring your store. This includes things such as setting the store's name, database connection, and Elasticsearch connection.

php artisan aero:install

This command runs you through the installation process for your store. This includes completing migrations, configuring Elasticsearch, linking storage, seeding data, and clearing the applications cache.

php artisan aero:update

This command updates all aerocommerce modules, can optionally run as a dry run (asked upon running command).

php artisan aero:link

This command calls the patch command, links theme/module assets, clears the Twig views cache, and clears the Laravel views cache.

php artisan module:install

This command will install a specific module after you have composer required the module. This command seeds any data for the module, optionally clears the application cache, and links the module assets. You should note that this command doesn’t have to be used and is automatically run when composer requiring a module.

php artisan theme:install

This command will install a specific theme.

php artisan aero:patch

This command patches your store's database. This command is run automatically when updating and ensures that your database is kept up to date with the latest updates.

php artisan aero:search:install

This command sets up the necessary environment for your search driver. By default when using Elasticsearch this command will create the required indexes.

php artisan aero:search:rebuild

This command resets up and reindexes your search by running the search install and reindex commands.

php artisan aero:search:reindex

This command will reindex your database data into your searches index. By default when using Elasticsearch this command will clear the index and then index your database data.

php artisan aero:seed:emails

This command will seed some default email templates to your mail notifications.

php artisan aero:settings:clear

This command will clear the settings cache file.

php artisan aero:sitemap:cms

This command will generate the pages-sitemap.xml sitemap file in your public storage directory. This sitemap covers any pages you create through the admin.

php artisan aero:sitemap:combinations

This command will generate the combinations-sitemap.xml file in your public storage directory. This sitemap covers any combinations you create through the admin.

php artisan aero:sitemap:generate

This command will generate a complete sitemap for your store. This generates a sitemap.xml file in your public storage directory that references individual sitemap files for products, combinations, and pages.

php artisan aero:sitemap:products

This command will generate the products-sitemap.xml sitemap file in your public storage directory. This sitemap covers any products you create through the admin.

php artisan aero:subscriptions:alert-upcoming

This command will look for upcoming subscriptions and emit the Aero\Subscription\Events\SubscriptionUpcoming event for them.

php artisan aero:subscriptions:check-cards

This command will look for expired cards on subscriptions and then cancel the subscription and emit the Aero\Subscription\Events\SubscriptionCardExpired event for them.

php artisan aero:subscriptions:process

This command will process any subscriptions that need processing.

Tutorials

Tutorials

How do I add a custom price to a product with a module?

In this mini tutorial, we’ll add checkboxes to a product page that allow customers to select additional charges. The product page will update the additional price dynamically as options are selected, and the total will continue to update when the product price changes, for example when a different variant is selected.

This tutorial assumes you already have a module set up, or are happy working directly from your app service provider using the boot method. See here for information on setting up a module.

1. Add the extra prices

We’ll start by defining the available extra prices in our module service provider. For this example, we’ll use an array as the source of truth rather than database records:

protected $prices = [
    [
        'id' => 1,
        'name' => 'Pay £1 more',
        'price' => [
            'inc' => 100,
            'ex' => 83.33,
        ],
    ],
    [
        'id' => 2,
        'name' => 'Pay £5 more',
        'price' => [
            'inc' => 500,
            'ex' => 416.66,
        ],
    ],
    [
        'id' => 3,
        'name' => 'Pay £10 more',
        'price' => [
            'inc' => 1000,
            'ex' => 833.33,
        ],
    ],
];

2. Add the prices data to the products page

Next, we’ll make the extra prices available to the product page and add the JavaScript that will handle the checkboxes.

First, create a javascript.twig view in your module's resources directory. We’ll populate this later.

We can then extend Aero\Store\Http\Responses\ProductPage to add the $prices data to the product page:

\Aero\Store\Http\Responses\ProductPage::extend(function (\Aero\Store\Http\Responses\ProductPage $page, \Closure $next) {
    $page->setData('extra_prices', $this->prices);

    \Aero\Store\Pipelines\ContentForBody::extend(function (string $content, \Closure $next) {
        $content .= view('acme::javascript');

        return $next($content);
    });

    return $next($page);
});

3. Add the extra price checkboxes

In product.twig, we can display the product's total price and the additional price selected by the customer:

Total Price
<p v-text="total_price.inc"></p>

Total Extra Price
<p v-text="additional_prices['extra-prices'].inc" v-if="additional_prices['extra-prices']"></p>

{% for extra in extra_prices %}
   <div>
       <input type="checkbox" id="extraPrice{{ extra.id }}" value="{{ extra.id }}" data-extra-price-inc="{{ extra.price.inc }}" data-extra-price-ex="{{ extra.price.ex }}">
       <label for="extraPrice{{ extra.id }}">&nbsp;{{ extra.name }}</label>
   </div>
{% endfor %}

4. Update the additional price with JavaScript

Now we can add the code to javascript.twig

There are two things we need to handle:

We’ll initialise the additional price when product.loaded fires as well. This means the calculation is also correct if any of the checkboxes are selected by default:

<script>
   window.AeroEvents.on('product.loaded', function () {
       var extraPriceElements = document.querySelectorAll('input[type="checkbox"][data-extra-price-inc]');

       for (var i = 0; i < extraPriceElements.length; i++) {
           extraPriceElements[i].addEventListener('input', updateAndSetAdditionalPrices);
       }

       function updateAndSetAdditionalPrices() {
           var price = { key: 'extra-prices', inc: 0, ex: 0 };
           var extraPriceElements = document.querySelectorAll('input[type=checkbox]:checked[data-extra-price-inc]');

           for (var i = 0; i < extraPriceElements.length; i++) {
               price.inc += parseInt(extraPriceElements[i].dataset.extraPriceInc);
               price.ex += parseInt(extraPriceElements[i].dataset.extraPriceEx);
           }

           window.Aero.runEvent('product.set-additional-price', price);
       }

       updateAndSetAdditionalPrices();
   });

   window.AeroEvents.on('product.add-to-cart', function (data) {
       var extras = [];
       var extraPriceElements = document.querySelectorAll('input[type=checkbox]:checked[data-extra-price-inc]');

       for (var i = 0; i < extraPriceElements.length; i++) {
           extras.push(extraPriceElements[i].value);
       }

       data.extras = extras;

       return data;
   });
</script>

5. Add the selected extras to the cart item

We can extend Aero\Store\Pipelines\CartItemBuilder and read the extras array from the request. Each ID is matched against the $prices array, with invalid IDs filtered out before creating the cart item options. 

Each valid extra becomes an Aero\Cart\CartItemOption with its corresponding inclusive price.

\Aero\Store\Pipelines\CartItemBuilder::extend(function (\Aero\Cart\CartItem $item) {
    collect(request()->input('extras', []))
        ->map(function ($extra) {
            return collect($this->prices)->firstWhere('id', $extra);
        })
        ->filter()
        ->each(function ($extra) use ($item) {
            $option = \Aero\Cart\CartItemOption::create($extra['name']);
            $option->setPriceInc($extra['price']['inc']);

            $item->addOption($option);
        });
});

Complete Service Provider

<?php

namespace Aerocargo\Acme;

use Aero\Cart\CartItem;
use Aero\Cart\CartItemOption;
use Aero\Common\Providers\ModuleServiceProvider;
use Aero\Store\Http\Responses\ProductPage;
use Aero\Store\Pipelines\CartItemBuilder;
use Aero\Store\Pipelines\ContentForBody;

class ServiceProvider extends ModuleServiceProvider
{
    protected $prices = [
        [
            'id' => 1,
            'name' => 'Pay £1 more',
            'price' => [
                'inc' => 100,
                'ex' => 83.33,
            ],
        ],
        [
            'id' => 2,
            'name' => 'Pay £5 more',
            'price' => [
                'inc' => 500,
                'ex' => 416.66,
            ],
        ],
        [
            'id' => 3,
            'name' => 'Pay £10 more',
            'price' => [
                'inc' => 1000,
                'ex' => 833.33,
            ],
        ],
    ];

    public function setup()
    {
        $this->loadViewsFrom(__DIR__.'/../resources/views', 'acme');

        ProductPage::extend(function (ProductPage $page) {
            $page->setData('extra_prices', $this->prices);

            ContentForBody::extend(function (&$content) {
                $content .= view('acme::javascript');
            });
        });

        CartItemBuilder::extend(function (CartItem $item) {
            collect(request()->input('extras', []))
                ->map(function ($extra) {
                    return collect($this->prices)->firstWhere('id', $extra);
                })
                ->filter()
                ->each(function ($extra) use ($item) {
                    $option = CartItemOption::create($extra['name']);
                    $option->setPriceInc($extra['price']['inc']);

                    $item->addOption($option);
                });
        });
    }
}