Full Code of aimeos/aimeos-laravel for AI

master 42e146cf3a43 cached
101 files
206.6 KB
66.5k tokens
259 symbols
1 requests
Download .txt
Showing preview only (229K chars total). Download the full file or copy to clipboard to get everything.
Repository: aimeos/aimeos-laravel
Branch: master
Commit: 42e146cf3a43
Files: 101
Total size: 206.6 KB

Directory structure:
gitextract_8j091w8s/

├── .circleci/
│   └── config.yml
├── .coveralls.yml
├── .devcontainer/
│   └── devcontainer.json
├── .gitattributes
├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.md
│       └── feature_request.md
├── .gitignore
├── LICENSE
├── README.md
├── build.xml
├── composer.json
├── config/
│   ├── default.php
│   └── shop.php
├── phpunit.xml.dist
├── routes/
│   └── aimeos.php
├── src/
│   ├── Base/
│   │   ├── Aimeos.php
│   │   ├── Config.php
│   │   ├── Context.php
│   │   ├── I18n.php
│   │   ├── Locale.php
│   │   ├── Shop.php
│   │   ├── Support.php
│   │   └── View.php
│   ├── Command/
│   │   ├── AbstractCommand.php
│   │   ├── AccountCommand.php
│   │   ├── ClearCommand.php
│   │   ├── JobsCommand.php
│   │   └── SetupCommand.php
│   ├── Composer.php
│   ├── Controller/
│   │   ├── AccountController.php
│   │   ├── AdminController.php
│   │   ├── BasketController.php
│   │   ├── CatalogController.php
│   │   ├── CheckoutController.php
│   │   ├── GraphqlController.php
│   │   ├── JqadmController.php
│   │   ├── JsonadmController.php
│   │   ├── JsonapiController.php
│   │   ├── PageController.php
│   │   ├── ResolveController.php
│   │   └── SupplierController.php
│   ├── Facades/
│   │   ├── Attribute.php
│   │   ├── Basket.php
│   │   ├── Catalog.php
│   │   ├── Cms.php
│   │   ├── Customer.php
│   │   ├── Locale.php
│   │   ├── Order.php
│   │   ├── Product.php
│   │   ├── Service.php
│   │   ├── Shop.php
│   │   ├── Stock.php
│   │   ├── Subscription.php
│   │   └── Supplier.php
│   ├── ShopServiceProvider.php
│   └── helpers.php
├── tests/
│   ├── AimeosTestAbstract.php
│   ├── Base/
│   │   ├── AimeosTest.php
│   │   ├── ConfigTest.php
│   │   ├── ContextTest.php
│   │   ├── I18nTest.php
│   │   ├── LocaleTest.php
│   │   ├── SupportTest.php
│   │   └── ViewTest.php
│   ├── Command/
│   │   ├── AccountCommandTest.php
│   │   ├── ClearCommandTest.php
│   │   ├── JobsCommandTest.php
│   │   └── SetupCommandTest.php
│   ├── Controller/
│   │   ├── AccountControllerTest.php
│   │   ├── AdminControllerTest.php
│   │   ├── BasketControllerTest.php
│   │   ├── CatalogControllerTest.php
│   │   ├── CheckoutControllerTest.php
│   │   ├── GraphqlControllerTest.php
│   │   ├── JqadmControllerTest.php
│   │   ├── JsonadmControllerTest.php
│   │   ├── JsonapiControllerTest.php
│   │   ├── PageControllerTest.php
│   │   ├── ResolveControllerTest.php
│   │   └── SupplierControllerTest.php
│   ├── FacadesTest.php
│   ├── HelpersTest.php
│   └── fixtures/
│       └── views/
│           └── app.blade.php
└── views/
    ├── account/
    │   └── index.blade.php
    ├── admin/
    │   └── index.blade.php
    ├── base.blade.php
    ├── basket/
    │   └── index.blade.php
    ├── catalog/
    │   ├── count.blade.php
    │   ├── detail.blade.php
    │   ├── home.blade.php
    │   ├── list.blade.php
    │   ├── session.blade.php
    │   ├── stock.blade.php
    │   ├── suggest.blade.php
    │   └── tree.blade.php
    ├── checkout/
    │   ├── confirm.blade.php
    │   ├── index.blade.php
    │   └── update.blade.php
    ├── jqadm/
    │   └── index.blade.php
    ├── page/
    │   └── index.blade.php
    └── supplier/
        └── detail.blade.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .circleci/config.yml
================================================
# PHP CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-php/ for more details
#
version: 2

jobs:
  "php82-mysql80":
    docker:
      - image: aimeos/ci-php:8.2
      - image: cimg/mysql:8.0
        environment:
          MYSQL_ROOT_PASSWORD: rootpw
          MYSQL_DATABASE: laravel
          MYSQL_USER: aimeos
          MYSQL_PASSWORD: aimeos
    steps:
      - checkout
      - run: wget https://getcomposer.org/download/latest-stable/composer.phar -O composer
      - run: php composer req "laravel/framework:~10.0" --no-update
      - run: php composer req "phpunit/phpunit:~10.0" --no-update --dev
      - restore_cache:
          keys:
            - php82-{{ checksum "composer.json" }}
      - run: php composer update -n --prefer-dist
      - save_cache:
          key: php82-{{ checksum "composer.json" }}
          paths: [./vendor]
      - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done
      - run: ./vendor/bin/phpunit

  "php83-mysql80":
    docker:
      - image: aimeos/ci-php:8.3
      - image: cimg/mysql:8.0
        environment:
          MYSQL_ROOT_PASSWORD: rootpw
          MYSQL_DATABASE: laravel
          MYSQL_USER: aimeos
          MYSQL_PASSWORD: aimeos
    steps:
      - checkout
      - run: wget https://getcomposer.org/download/latest-stable/composer.phar -O composer
      - run: php composer req "laravel/framework:~11.0" --no-update
      - run: php composer req "phpunit/phpunit:~11.0" --no-update --dev
      - restore_cache:
          keys:
            - php83-{{ checksum "composer.json" }}
      - run: php composer update -n --prefer-dist
      - save_cache:
          key: php83-{{ checksum "composer.json" }}
          paths: [./vendor]
      - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done
      - run: ./vendor/bin/phpunit

  "php84-mysql80":
    docker:
      - image: aimeos/ci-php:8.4
      - image: cimg/mysql:8.0
        environment:
          MYSQL_ROOT_PASSWORD: rootpw
          MYSQL_DATABASE: laravel
          MYSQL_USER: aimeos
          MYSQL_PASSWORD: aimeos
    steps:
      - checkout
      - run: wget https://getcomposer.org/download/latest-stable/composer.phar -O composer
      - run: php composer req "laravel/framework:~12.0" --no-update
      - run: php composer req "phpunit/phpunit:~11.0" --no-update --dev
      - restore_cache:
          keys:
            - php84-{{ checksum "composer.json" }}
      - run: php composer update -n --prefer-dist
      - save_cache:
          key: php84-{{ checksum "composer.json" }}
          paths: [./vendor]
      - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done
      - run: ./vendor/bin/phpunit --coverage-clover coverage.xml

  "php85-mysql80":
    docker:
      - image: aimeos/ci-php:8.5
      - image: cimg/mysql:8.0
        environment:
          MYSQL_ROOT_PASSWORD: rootpw
          MYSQL_DATABASE: laravel
          MYSQL_USER: aimeos
          MYSQL_PASSWORD: aimeos
    steps:
      - checkout
      - run: wget https://getcomposer.org/download/latest-stable/composer.phar -O composer
      - run: php composer req "laravel/framework:~13.0" --no-update
      - run: php composer req "phpunit/phpunit:~12.0" --no-update --dev
      - restore_cache:
          keys:
            - php84-{{ checksum "composer.json" }}
      - run: php composer update -n --prefer-dist
      - save_cache:
          key: php84-{{ checksum "composer.json" }}
          paths: [./vendor]
      - run: for i in `seq 1 10`; do nc -z 127.0.0.1 3306 && echo OK && exit 0; echo -n .; sleep 1; done
      - run: ./vendor/bin/phpunit --coverage-clover coverage.xml

workflows:
  version: 2
  unittest:
    jobs:
      - "php82-mysql80"
      - "php83-mysql80"
      - "php84-mysql80"
      - "php85-mysql80"


================================================
FILE: .coveralls.yml
================================================
src_dir: ./
json_path: coveralls.json
coverage_clover: coverage.xml


================================================
FILE: .devcontainer/devcontainer.json
================================================
{
  "image": "mcr.microsoft.com/devcontainers/universal:2",
  "features": {
  }
}


================================================
FILE: .gitattributes
================================================
/.gitattributes export-ignore
/.github export-ignore
/.gitignore export-ignore
/build.xml export-ignore
/phpunit.xml.dist export-ignore
/tests export-ignore


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve

---

**Environment**
1. Version (e.g. 2020.10)
2. Operating system (Linux, Mac, Windows)

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project

---

**Is your feature request related to a problem?**
A clear and concise description of what the problem is.

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
Any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .gitignore
================================================
/vendor
composer.lock
.phpunit.result.cache
.phpunit.cache
.idea/
node_modules
phpunit.xml


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2015 Aimeos

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



================================================
FILE: README.md
================================================
<a href="https://aimeos.org/">
    <img src="https://aimeos.org/fileadmin/template/icons/logo.png" alt="Aimeos logo" title="Aimeos" align="right" height="60" />
</a>

# Aimeos Laravel ecommerce package
[![Total Downloads](https://poser.pugx.org/aimeos/aimeos-laravel/d/total.svg)](https://packagist.org/packages/aimeos/aimeos-laravel)
[![Build Status](https://circleci.com/gh/aimeos/aimeos-laravel.svg?style=shield)](https://circleci.com/gh/aimeos/aimeos-laravel)
[![Coverage Status](https://coveralls.io/repos/aimeos/aimeos-laravel/badge.svg?branch=master&service=github)](https://coveralls.io/github/aimeos/aimeos-laravel?branch=master)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/aimeos/aimeos-laravel/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/aimeos/aimeos-laravel/?branch=master)
[![License](https://poser.pugx.org/aimeos/aimeos/license.svg)](https://packagist.org/packages/aimeos/aimeos)

:star: Star us on GitHub — it motivates us a lot! 😀

[Aimeos](https://aimeos.org/Laravel) is THE professional, full-featured and
ultra fast Laravel ecommerce package!  You can install it in your
existing Laravel application within 5 minutes and can adapt, extend, overwrite
and customize anything to your needs.

[![Aimeos Laravel demo](https://aimeos.org/fileadmin/aimeos.org/images/aimeos-github.png)](https://laravel.demo.aimeos.org/)

## Features

Aimeos is a full-featured e-commerce package:

* Multi vendor, multi channel and multi warehouse
* From one to 1,000,000,000+ items
* Extremly fast down to 20ms
* For multi-tentant e-commerce SaaS solutions with unlimited vendors
* Bundles, vouchers, virtual, configurable, custom and event products
* Subscriptions with recurring payments
* 100+ payment gateways
* Full RTL support (frontend and backend)
* Block/tier pricing out of the box
* Extension for customer/group based prices
* Discount and voucher support
* Flexible basket rule system
* Full-featured admin backend
* Beautiful admin dashboard
* Configurable product data sets
* JSON REST API based on jsonapi.org
* GraphQL API for administration
* Completly modular structure
* Extremely configurable and extensible
* Extension for market places with millions of vendors
* Fully SEO optimized including rich snippets
* Translated to 30+ languages
* AI-based text translation
* Optimized for smart phones and tablets
* Secure and reviewed implementation
* High quality source code

... and [more Aimeos features](https://aimeos.org/features)

Supported languages:


<p align="center" style="display: inline;">
    <a href="https://www.transifex.com/aimeos/"><img src="https://flagicons.lipis.dev/flags/4x3/us.svg" title="English" width="24"></a>
    <a href="https://www.transifex.com/aimeos/dashboard/all_projects/de/"><img src="https://flagicons.lipis.dev/flags/4x3/de.svg" title="German" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/fr/"><img src="https://flagicons.lipis.dev/flags/4x3/fr.svg" title="French" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/es/"><img src="https://flagicons.lipis.dev/flags/4x3/es.svg" title="Spanish" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/nl/"><img src="https://flagicons.lipis.dev/flags/4x3/nl.svg" title="Dutch" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/it/"><img src="https://flagicons.lipis.dev/flags/4x3/it.svg" title="Italian" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/pt/"><img src="https://flagicons.lipis.dev/flags/4x3/pt.svg" title="Portuguese" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/da/"><img src="https://flagicons.lipis.dev/flags/4x3/dk.svg" title="Danish" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/fi/"><img src="https://flagicons.lipis.dev/flags/4x3/fi.svg" title="Finnish" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/sv/"><img src="https://flagicons.lipis.dev/flags/4x3/sv.svg" title="Swedish" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/no/"><img src="https://flagicons.lipis.dev/flags/4x3/no.svg" title="Norwegian" width="24"></a>
    &nbsp;&nbsp;&nbsp;
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/pl/"><img src="https://flagicons.lipis.dev/flags/4x3/pl.svg" title="Polish" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/hu/"><img src="https://flagicons.lipis.dev/flags/4x3/hu.svg" title="Hungarian" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/ru/"><img src="https://flagicons.lipis.dev/flags/4x3/ru.svg" title="Russian" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/uk/"><img src="https://flagicons.lipis.dev/flags/4x3/ua.svg" title="Ukrainian" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/hr/"><img src="https://flagicons.lipis.dev/flags/4x3/hr.svg" title="Croatian" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/sl/"><img src="https://flagicons.lipis.dev/flags/4x3/si.svg" title="Slovenian" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/ro/"><img src="https://flagicons.lipis.dev/flags/4x3/ro.svg" title="Romanian" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/cs/"><img src="https://flagicons.lipis.dev/flags/4x3/cz.svg" title="Czech" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/sr/"><img src="https://flagicons.lipis.dev/flags/4x3/sr.svg" title="Serbian" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/sk/"><img src="https://flagicons.lipis.dev/flags/4x3/sk.svg" title="Slovak" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/et/"><img src="https://flagicons.lipis.dev/flags/4x3/et.svg" title="Estonian" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/lv/"><img src="https://flagicons.lipis.dev/flags/4x3/lv.svg" title="Latvian" width="24"></a>
    &nbsp;&nbsp;&nbsp;
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/tr/"><img src="https://flagicons.lipis.dev/flags/4x3/tr.svg" title="Turkish" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/ar/"><img src="https://flagicons.lipis.dev/flags/4x3/sa.svg" title="Arabic" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/fa/"><img src="https://flagicons.lipis.dev/flags/4x3/ir.svg" title="Persian" width="24"></a>
    &nbsp;&nbsp;&nbsp;
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/zh/"><img src="https://flagicons.lipis.dev/flags/4x3/cn.svg" title="Chinese" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/ja/"><img src="https://flagicons.lipis.dev/flags/4x3/jp.svg" title="Japanese" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/id/"><img src="https://flagicons.lipis.dev/flags/4x3/id.svg" title="Indonesian" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/vi/"><img src="https://flagicons.lipis.dev/flags/4x3/vi.svg" title="Vietnamese" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/my/"><img src="https://flagicons.lipis.dev/flags/4x3/my.svg" title="Burmese" width="24"></a>
     <a href="https://www.transifex.com/aimeos/dashboard/all_projects/ko/"><img src="https://flagicons.lipis.dev/flags/4x3/kr.svg" title="Korean" width="24"></a>
</p>


Check out the demos:

* [Aimeos frontend demo](https://laravel.demo.aimeos.org)
* [Aimeos admin demo](https://admin.demo.aimeos.org)

## Alternatives

### Full shop application

If you want to set up a new application or test Aimeos, we recommend the Aimeos
shop distribution. It contains everything for a quick start and you will get a
fully working online shop in less than 5 minutes:

:star: [Aimeos shop distribution](https://github.com/aimeos/aimeos)

### Headless distribution

If you want to build a single page application (SPA) respectively a progressive web
application (PWA) yourself and don't need the Aimeos HTML frontend, then the Aimeos
headless distribution is the right choice:

:star:  [Aimeos headless distribution](https://github.com/aimeos/aimeos-headless)

## Table of content

- [Supported versions](#supported-versions)
- [Requirements](#requirements)
- [Database](#database)
- [Installation](#installation)
- [Authentication](#authentication)
- [Setup](#setup)
- [Test](#test)
- [Hints](#hints)
- [License](#license)
- [Links](#links)

## Supported versions

Currently, the Aimeos Laravel packages **2024.10 and later** are fully supported:

- LTS release: 2025.10+ (Laravel 10.x, 11.x and 12.x)
- old LTS release: 2024.10+ (Laravel 10.x and 11.x)

If you want to upgrade between major versions, please have a look into the
[upgrade guide](https://aimeos.org/docs/latest/laravel/setup/#upgrade)!

## Requirements

The Aimeos shop distribution requires:
- Linux/Unix, WAMP/XAMP or MacOS environment
- PHP >= 8.1
- MySQL >= 5.7.8, MariaDB >= 10.2.2, PostgreSQL 9.6+, SQL Server 2019+
- Web server (Apache, Nginx or integrated PHP web server for testing)

If required PHP extensions are missing, `composer` will tell you about the missing
dependencies.

If you want to upgrade between major versions, please have a look into the
[upgrade guide](https://aimeos.org/docs/latest/laravel/setup/#upgrade)!

## Database

Make sure that you've **created the database** in advance and added the configuration
to the `.env` file in your application directory. Sometimes, using the .env file makes
problems and you will get exceptions that the connection to the database failed. In that
case, add the database credentials to the **resource/db section of your ./config/shop.php**
file too!

If you don't have at least MySQL 5.7.8 or MariaDB 10.2.2 installed, you will probably get an error like

```
Specified key was too long; max key length is 767 bytes
```

To circumvent this problem, drop the new tables if there have been any created and
change the charset/collation setting in `./config/database.php` to these values before
installing Aimeos again:

```php
'connections' => [
    'mysql' => [
        // ...
        'charset' => 'utf8',
        'collation' => 'utf8_unicode_ci',
        // ...
    ]
]
```

**Caution:** Also make sure that your MySQL server creates *InnoDB* tables by default as *MyISAM*
tables won't work and will result in an foreign key constraint error!

If you want to use a database server other than MySQL, please have a look into the article about
[supported database servers](https://aimeos.org/docs/latest/infrastructure/databases/)
and their specific configuration. Supported are:

* MySQL, MariaDB (fully)
* PostgreSQL (fully)
* SQL Server (fully)

Make sure, you use one of the supported database servers in your `.env` file, e.g.:

```
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=aimeos
DB_USERNAME=root
DB_PASSWORD=
```

**Caution:** The SQLite database configured by default is **NOT supported!**

## Installation

The Aimeos Laravel online shop package is a composer based library. It can be
installed easiest by using [Composer 2.1+](https://getcomposer.org) in the root
directory of your existing Laravel application:

```
wget https://getcomposer.org/download/latest-stable/composer.phar -O composer
```

Then, add these lines to the composer.json of the **Laravel skeleton application**:

```json
    "prefer-stable": true,
    "minimum-stability": "dev",
    "require": {
        "aimeos/aimeos-laravel": "~2025.10",
        ...
    },
    "scripts": {
        "post-update-cmd": [
            "@php artisan vendor:publish --tag=laravel-assets --ansi --force",
            "@php artisan vendor:publish --tag=public --ansi",
            "\\Aimeos\\Shop\\Composer::join"
        ],
        ...
    }
```

Afterward, install the Aimeos shop package using

`php composer update -W`

In the last step, you must now execute these artisan commands to get a working
or updated Aimeos installation:

```bash
php artisan vendor:publish --tag=config --tag=public
php artisan migrate
php artisan aimeos:setup --option=setup/default/demo:1
```

In a production environment or if you don't want that the demo data gets
installed, leave out the `--option=setup/default/demo:1` option.

## Authentication

You have to set up one of Laravel's authentication starter kits. Laravel Breeze
is the easiest one but you can also use Jetstream.

```bash
composer require laravel/breeze
php artisan breeze:install
npm install && npm run build # if not executed automatically by the previous command
```

Laravel Breeze will ask you a few questions, the most important one is the type of stack you
want to use. Select "Blade" (it's the easiest way) and use the default values for the others.

It also adds a route for `/profile` to `./routes/web.php` which may overwrite the
`aimeos_shop_account` route. To avoid an exception about a missing `aimeos_shop_account`
route, change the URL for these lines from `./routes/web.php` file from `/profile` to
`/profile/me`:

```php
Route::middleware('auth')->group(function () {
    Route::get('/profile/me', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile/me', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile/me', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
```

For more information, please follow the Laravel documentation:
* [Laravel 12.x](https://laravel.com/docs/12.x/authentication)
* [Laravel 11.x](https://laravel.com/docs/11.x/authentication)
* [Laravel 10.x](https://laravel.com/docs/10.x/authentication)

### Configure authentication

As a last step, you need to extend the `boot()` method of your
`App\Providers\AppServiceProvider` class and add the lines to define how
authorization for "admin" is checked in `app/Providers/AppServiceProvider.php`:

```php
    public function boot()
    {
        // Keep the lines before

        \Illuminate\Support\Facades\Gate::define('admin', function($user, $class, $roles) {
            if( isset( $user->superuser ) && $user->superuser ) {
                return true;
            }
            return app( '\Aimeos\Shop\Base\Support' )->checkUserGroup( $user, $roles );
        });
    }
```

### Create account

Test if your authentication setup works before you continue. Create an admin account
for your Laravel application so you will be able to log into the Aimeos admin interface:

```bash
php artisan aimeos:account --super <email>
```

The e-mail address is the user name for login and the account will work for the
frontend too. To protect the new account, the command will ask you for a password.
The same command can create limited accounts by using `--admin`, `--editor` or `--api`
instead of `--super` (access to everything).

## Setup

To reference images correctly, you have to adapt your `.env` file and set the `APP_URL`
to your real URL, e.g.

```
APP_URL=http://127.0.0.1:8000
```

**Caution:** Make sure, Laravel uses the `file` session driver in your `.env` file!
Otherwise, the shopping basket content won't get stored correctly!

```
SESSION_DRIVER=file
```

If your `./public` directory isn't writable by your web server, you have to create these
directories:

```
mkdir public/aimeos public/vendor
chmod 777 public/aimeos public/vendor
```

In a production environment, you should be more specific about the granted permissions!

## Test

Then, you should be able to call the catalog list page in your browser. For a
quick start, you can use the integrated web server. Simply execute this command
in the base directory of your application:

```
php artisan serve
```

### Frontend

Point your browser to the list page of the shop using:

http://127.0.0.1:8000/shop/search

**Note:** Integrating the Aimeos package adds some routes like `/shop` or `/admin` to your
Laravel installation but the **home page stays untouched!** If you want to add Aimeos to
the home page as well, replace the route for "/" in `./routes/web.php` by this line:

```php
Route::group(['middleware' => ['web']], function () {
    Route::get('/', '\Aimeos\Shop\Controller\CatalogController@homeAction')->name('aimeos_home');
});
```

For multi-vendor setups, read the article about [multiple shops](https://aimeos.org/docs/latest/laravel/customize/#multiple-shops).

This will display the Aimeos catalog home component on the home page you you get a
nice looking shop home page which will look like this:

[![Aimeos frontend](https://aimeos.org/fileadmin/aimeos.org/images/aimeos-frontend.jpg?2021.07)](http://127.0.0.1:8000/)

### Backend

If you've still started the internal PHP web server (`php artisan serve`)
you should now open this URL in your browser:

http://127.0.0.1:8000/admin

Enter the e-mail address and the password of the newly created user and press "Login".
If you don't get redirected to the admin interface (that depends on the authentication
code you've created according to the Laravel documentation), point your browser to the
`/admin` URL again.

**Caution:** Make sure that you aren't already logged in as a non-admin user! In this
case, login won't work because Laravel requires you to log out first.

[![Aimeos backend](https://aimeos.org/fileadmin/aimeos.org/images/aimeos-backend.png)](http://127.0.0.1:8000/admin)

## Hints

To simplify development, you should configure to use no content cache. You can
do this in the `config/shop.php` file of your Laravel application by adding
these lines at the bottom:

```php
    'madmin' => [
        'cache' => [
            'manager' => [
                'name' => 'None',
            ],
        ],
    ],
```

## License

The Aimeos Laravel package is licensed under the terms of the MIT license and
is available for free.

## Links

* [Web site](https://aimeos.org/Laravel)
* [Documentation](https://aimeos.org/docs/Laravel)
* [Forum](https://aimeos.org/help/laravel-package-f18/)
* [Issue tracker](https://github.com/aimeos/aimeos-laravel/issues)
* [Composer packages](https://packagist.org/packages/aimeos/aimeos-laravel)
* [Source code](https://github.com/aimeos/aimeos-laravel)


================================================
FILE: build.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>

<project name="Aimeos Laravel package" default="update">

	<target name="update" description="Updates the bundle">
		<exec command="git pull https://github.com/aimeos/aimeos-laravel.git" checkreturn="true" logoutput="true" />
		<exec command="composer update --working-dir ${project.basedir}" checkreturn="true" logoutput="true" />
	</target>


	<target name="release" description="Creates new release">
		<propertyprompt propertyName="version" promptText="Enter release version" promptCharacter=":" useExistingValue="true"/>
		<exec command="git branch ${version}" checkreturn="true" logoutput="true" />
		<exec command="git tag -a aimeos-laravel_${version} -m 'Release ${version}'" checkreturn="true" logoutput="true" />
		<exec command="git push origin ${version}" checkreturn="true" logoutput="true" />
		<exec command="git push --tags" checkreturn="true" logoutput="true" />
	</target>

</project>


================================================
FILE: composer.json
================================================
{
    "name": "aimeos/aimeos-laravel",
    "description": "Cloud native, API first Laravel eCommerce package with integrated AI for ultra-fast online shops, marketplaces and complex B2B projects",
    "homepage": "https://aimeos.org/Laravel",
    "type": "laravel-package",
    "license": "MIT",
    "keywords": ["aimeos", "laravel", "e-commerce", "ecommerce", "B2B", "shop", "portal", "marketplace", "API", "JSON", "GraphQL"],
    "support": {
        "source": "https://github.com/Aimeos/aimeos-laravel",
        "issues": "https://github.com/Aimeos/aimeos-laravel/issues",
        "forum": "https://aimeos.org/help",
        "wiki": "https://aimeos.org/docs"
    },
    "prefer-stable": true,
    "minimum-stability": "dev",
    "require": {
        "composer-runtime-api": "^2.1",
        "laravel/framework": "^10.0||^11.0||^12.0||^13.0",
        "symfony/psr-http-message-bridge": "~6.0||~7.0",
        "laminas/laminas-diactoros": "~2.5||~3.0",
        "nyholm/psr7": "~1.2",
        "aimeos/aimeos-core": "dev-master",
        "aimeos/ai-laravel": "dev-master",
        "aimeos/ai-admin-graphql": "dev-master",
        "aimeos/ai-admin-jqadm": "dev-master",
        "aimeos/ai-admin-jsonadm": "dev-master",
        "aimeos/ai-client-html": "dev-master",
        "aimeos/ai-client-jsonapi": "dev-master",
        "aimeos/ai-cms-grapesjs": "dev-master",
        "aimeos/ai-controller-jobs": "dev-master",
        "aimeos/ai-controller-frontend": "dev-master"
    },
    "require-dev": {
        "phpunit/phpunit": "^10.0||^11.0||^12.0",
        "orchestra/testbench": "~8.0||~9.0||~10.0||~11.0",
        "orchestra/testbench-browser-kit": "~8.0||~9.0||~10.0||~11.0",
        "php-coveralls/php-coveralls": "~2.0"
    },
    "autoload": {
        "psr-4": {
            "Aimeos\\Shop\\": "src/"
        },
        "files": [
            "src/helpers.php"
        ]
    },
    "autoload-dev": {
        "classmap": [
            "tests/AimeosTestAbstract.php",
            "tests/HelpersTest.php"
        ]
    },
    "extra": {
        "laravel": {
            "providers": [
                "Aimeos\\Shop\\ShopServiceProvider"
            ]
        }
    }
}


================================================
FILE: config/default.php
================================================
<?php

switch( config( 'database.default', 'mysql' ) ) {
	case 'pgsql': $aimeosIndexManagerName = 'PgSQL'; break;
	case 'sqlsrv': $aimeosIndexManagerName = 'SQLSrv'; break;
	default: $aimeosIndexManagerName = 'MySQL';
}


return [

	'apc_enabled' => false,
	'apc_prefix' => 'laravel:',
	'pcntl_max' => 4,
	'pcntl_priority' => 19,

	'page' => [
		'account-index' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'account/profile', 'account/review', 'account/subscription', 'account/basket', 'account/history', 'account/favorite', 'account/watch', 'catalog/session'],
		'basket-index' => ['locale/select', 'catalog/tree', 'catalog/search', 'basket/standard', 'basket/bulk', 'basket/related'],
		'catalog-count' => ['catalog/count'],
		'catalog-detail' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'catalog/stage', 'catalog/detail', 'catalog/session'],
		'catalog-home' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'catalog/home'],
		'catalog-list' => ['locale/select', 'basket/mini', 'catalog/filter', 'catalog/tree', 'catalog/search', 'catalog/price', 'catalog/supplier', 'catalog/attribute', 'catalog/session', 'catalog/stage', 'catalog/lists'],
		'catalog-session' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'catalog/session'],
		'catalog-stock' => ['catalog/stock'],
		'catalog-suggest' => ['catalog/suggest'],
		'catalog-tree' => ['locale/select', 'basket/mini', 'catalog/filter', 'catalog/tree', 'catalog/search', 'catalog/price', 'catalog/supplier', 'catalog/attribute', 'catalog/session', 'catalog/stage', 'catalog/lists'],
		'checkout-confirm' => ['catalog/tree', 'catalog/search', 'checkout/confirm'],
		'checkout-index' => ['locale/select', 'catalog/tree', 'catalog/search', 'checkout/standard'],
		'checkout-update' => ['checkout/update'],
		'supplier-detail' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'supplier/detail', 'catalog/lists'],
	],

	'admin' => [
		'graphql' => [
			'url' => [
				'target' => 'aimeos_shop_graphql_post',
				'config' => [
					'absoluteUri' => true,
				],
			],
		],
		'jqadm' => [
			'url' => [
				'batch' => [
					'target' => 'aimeos_shop_jqadm_batch'
				],
				'copy' => [
					'target' => 'aimeos_shop_jqadm_copy'
				],
				'create' => [
					'target' => 'aimeos_shop_jqadm_create'
				],
				'delete' => [
					'target' => 'aimeos_shop_jqadm_delete'
				],
				'export' => [
					'target' => 'aimeos_shop_jqadm_export'
				],
				'get' => [
					'target' => 'aimeos_shop_jqadm_get'
				],
				'import' => [
					'target' => 'aimeos_shop_jqadm_import'
				],
				'save' => [
					'target' => 'aimeos_shop_jqadm_save'
				],
				'search' => [
					'target' => 'aimeos_shop_jqadm_search'
				],
			],
		],
		'jsonadm' => [
			'url' => [
				'target' => 'aimeos_shop_jsonadm_get',
				'config' => [
					'absoluteUri' => true,
				],
				'options' => [
					'target' => 'aimeos_shop_jsonadm_options',
					'config' => [
						'absoluteUri' => true,
					],
				],
			],
		],
	],
	'client' => [
		'html' => [
			'account' => [
				'index' => [
					'url' => [
						'target' => 'aimeos_shop_account',
					],
				],
				'basket' => [
					'url' => [
						'target' => 'aimeos_shop_account',
					],
				],
				'review' => [
					'url' => [
						'target' => 'aimeos_shop_account',
					],
				],
				'profile' => [
					'url' => [
						'target' => 'aimeos_shop_account',
					],
				],
				'subscription' => [
					'url' => [
						'target' => 'aimeos_shop_account',
					],
				],
				'history' => [
					'url' => [
						'target' => 'aimeos_shop_account',
					],
				],
				'favorite' => [
					'url' => [
						'target' => 'aimeos_shop_account_favorite',
					],
				],
				'watch' => [
					'url' => [
						'target' => 'aimeos_shop_account_watch',
					],
				],
				'download' => [
					'url' => [
						'target' => 'aimeos_shop_account_download',
					],
					'error' => [
						'url' => [
							'target' => 'aimeos_shop_account',
						],
					],
				],
			],
			'cms' => [
				'page' => [
					'url' => [
						'target' => 'aimeos_page',
					],
				],
			],
			'catalog' => [
				'count' => [
					'url' => [
						'target' => 'aimeos_shop_count',
					],
				],
				'detail' => [
					'url' => [
						'target' => 'aimeos_shop_detail',
						'filter' => ['path', 'd_prodid'],
					],
				],
				'home' => [
					'url' => [
						'target' => 'aimeos_home',
					],
				],
				'lists' => [
					'url' => [
						'target' => 'aimeos_shop_list',
					],
				],
				'session' => [
					'pinned' => [
						'url' => [
							'target' => 'aimeos_shop_session_pinned',
						],
					],
				],
				'stock' => [
					'url' => [
						'target' => 'aimeos_shop_stock',
					],
				],
				'suggest' => [
					'url' => [
						'target' => 'aimeos_shop_suggest',
					],
				],
				'tree' => [
					'url' => [
						'target' => 'aimeos_shop_tree',
						'filter' => ['path'],
					],
				],
			],
			'common' => [
				'template' => [
					'baseurl' => public_path( 'vendor/shop/themes/default' ),
				],
			],
			'basket' => [
				'standard' => [
					'url' => [
						'target' => 'aimeos_shop_basket',
					],
				],
			],
			'checkout' => [
				'confirm' => [
					'url' => [
						'target' => 'aimeos_shop_confirm',
					],
				],
				'standard' => [
					'url' => [
						'target' => 'aimeos_shop_checkout',
					],
					'summary' => [
						'option' => [
							'terms' => [
								'url' => [
									'target' => 'aimeos_page',
								],
								'privacy' => [
									'url' => [
										'target' => 'aimeos_page',
									],
								],
								'cancel' => [
									'url' => [
										'target' => 'aimeos_page',
									],
								],
							],
						],
					],
				],
				'update' => [
					'url' => [
						'target' => 'aimeos_shop_update',
					],
				],
			],
			'locale' => [
				'select' => [
					'currency' => [
						'param-name' => 'currency',
					],
					'language' => [
						'param-name' => 'locale',
					],
				],
			],
			'supplier' => [
				'detail' => [
					'url' => [
						'target' => 'aimeos_shop_supplier',
					],
				],
			]
		],
		'jsonapi' => [
			'url' => [
				'target' => 'aimeos_shop_jsonapi_options',
				'config' => [
					'absoluteUri' => true,
				],
			],
		],
	],

	'controller' => [
		'jobs' => [
			'to-email' => config( 'mail.from.address' ),
		]
	],

	'mshop' => [
		'customer' => [
			'manager' => [
				'name' => 'Laravel',
				'password' => [
					'name' => 'Bcrypt',
				],
			],
		],
		'index' => [
			'manager' => [
				'name' => $aimeosIndexManagerName,
			],
		],
	],
];


================================================
FILE: config/shop.php
================================================
<?php

return [

	'apc_enabled' => false, // enable for maximum performance if APCu is available
	'apc_prefix' => 'laravel:', // prefix for caching config and translation in APCu
	'num_formatter' => 'Locale', // locale based number formatter (alternative: "Standard")
	'pcntl_max' => 4, // maximum number of parallel command line processes when starting jobs
	'version' => env( 'APP_VERSION', 1 ), // shop CSS/JS file version
	'roles' => ['admin', 'editor'], // user groups allowed to access the admin backend
	'panel' => 'dashboard', // panel shown in admin backend after login

	'routes' => [
		// Docs: https://aimeos.org/docs/latest/laravel/extend/#custom-routes
		// Multi-sites: https://aimeos.org/docs/latest/laravel/customize/#multiple-shops
		// 'admin' => ['prefix' => 'admin', 'middleware' => ['web']],
		// 'jqadm' => ['prefix' => 'admin/{site}/jqadm', 'middleware' => ['web', 'auth']],
		// 'graphql' => ['prefix' => 'admin/{site}/graphql', 'middleware' => ['web', 'auth']],
		// 'jsonadm' => ['prefix' => 'admin/{site}/jsonadm', 'middleware' => ['web', 'auth']],
		// 'jsonapi' => ['prefix' => 'jsonapi', 'middleware' => ['web', 'api']],
		// 'account' => ['prefix' => 'profile', 'middleware' => ['web', 'auth']],
		// 'default' => ['prefix' => 'shop', 'middleware' => ['web']],
		// 'basket' => ['prefix' => 'shop', 'middleware' => ['web']],
		// 'checkout' => ['prefix' => 'shop', 'middleware' => ['web']],
		// 'confirm' => ['prefix' => 'shop', 'middleware' => ['web']],
		// 'supplier' => ['prefix' => 's', 'middleware' => ['web']],
		// 'page' => ['prefix' => 'p', 'middleware' => ['web']],
		// 'home' => ['middleware' => ['web']],
		// 'update' => [],
	],

	'page' => [
		'account-index' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'account/profile', 'account/review', 'account/subscription', 'account/basket', 'account/history', 'account/favorite', 'account/watch', 'catalog/session'],
		'basket-index' => ['locale/select', 'catalog/tree', 'catalog/search', 'basket/standard', 'basket/bulk', 'basket/related'],
		'catalog-count' => ['catalog/count'],
		'catalog-detail' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'catalog/stage', 'catalog/detail', 'catalog/session'],
		'catalog-home' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'catalog/home'],
		'catalog-list' => ['locale/select', 'basket/mini', 'catalog/filter', 'catalog/tree', 'catalog/search', 'catalog/price', 'catalog/supplier', 'catalog/attribute', 'catalog/session', 'catalog/stage', 'catalog/lists'],
		'catalog-session' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'catalog/session'],
		'catalog-stock' => ['catalog/stock'],
		'catalog-suggest' => ['catalog/suggest'],
		'catalog-tree' => ['locale/select', 'basket/mini', 'catalog/filter', 'catalog/tree', 'catalog/search', 'catalog/price', 'catalog/supplier', 'catalog/attribute', 'catalog/session', 'catalog/stage', 'catalog/lists'],
		'checkout-confirm' => ['catalog/tree', 'catalog/search', 'checkout/confirm'],
		'checkout-index' => ['locale/select', 'catalog/tree', 'catalog/search', 'checkout/standard'],
		'checkout-update' => ['checkout/update'],
		'supplier-detail' => ['locale/select', 'basket/mini', 'catalog/tree', 'catalog/search', 'supplier/detail', 'catalog/lists'],
		'cms' => ['cms/page', 'catalog/tree', 'basket/mini'],
	],

	'resource' => [
		'db' => [
			'adapter' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.driver', 'mysql' ),
			'host' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.host', '127.0.0.1' ),
			'port' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.port', '3306' ),
			'socket' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.unix_socket', '' ),
			'database' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.database', 'forge' ),
			'username' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.username', 'forge' ),
			'password' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.password', '' ),
			'stmt' => config( 'database.default', 'mysql' ) === 'mysql' ? ["SET SESSION sort_buffer_size=2097144; SET NAMES 'utf8mb4'; SET SESSION sql_mode='ANSI'"] : [],
			'limit' => 3, // maximum number of concurrent database connections
			'defaultTableOptions' => [
				'charset' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.charset' ),
				'collate' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.collation' ),
			],
			'driverOptions' => config( 'database.connections.' . config( 'database.default', 'mysql' ) . '.options' ),
		],
		'fs' => [
			'adapter' => 'Standard',
			'tempdir' => storage_path( 'tmp' ),
			'basedir' => public_path(),
			'baseurl' => rtrim(env('ASSET_URL', PHP_SAPI == 'cli' ? env('APP_URL') : ''), '/'),
		],
		'fs-media' => [
			'adapter' => 'Standard',
			'tempdir' => storage_path( 'tmp' ),
			'basedir' => public_path( 'aimeos' ),
			'baseurl' => rtrim(env('ASSET_URL', PHP_SAPI == 'cli' ? env('APP_URL') : ''), '/') . '/aimeos',
		],
		'fs-mimeicon' => [
			'adapter' => 'Standard',
			'tempdir' => storage_path( 'tmp' ),
			'basedir' => public_path( 'vendor/shop/mimeicons' ),
			'baseurl' => rtrim(env('ASSET_URL', PHP_SAPI == 'cli' ? env('APP_URL') : ''), '/') . '/vendor/shop/mimeicons',
		],
		'fs-theme' => [
			'adapter' => 'Standard',
			'tempdir' => storage_path( 'tmp' ),
			'basedir' => public_path( 'vendor/shop/themes' ),
			'baseurl' => rtrim(env('ASSET_URL', PHP_SAPI == 'cli' ? env('APP_URL') : ''), '/') . '/vendor/shop/themes',
		],
		'fs-admin' => [
			'adapter' => 'Standard',
			'tempdir' => storage_path( 'tmp' ),
			'basedir' => storage_path( 'admin' ),
		],
		'fs-export' => [
			'adapter' => 'Standard',
			'tempdir' => storage_path( 'tmp' ),
			'basedir' => storage_path( 'export' ),
		],
		'fs-import' => [
			'adapter' => 'Standard',
			'tempdir' => storage_path( 'tmp' ),
			'basedir' => storage_path( 'import' ),
		],
		'fs-secure' => [
			'adapter' => 'Standard',
			'tempdir' => storage_path( 'tmp' ),
			'basedir' => storage_path( 'secure' ),
		],
		'mq' => [
			'adapter' => 'Standard',
			'db' => 'db',
		],
		'email' => [
			'from-email' => config( 'mail.from.address' ),
			'from-name' => config( 'mail.from.name' ),
		],
	],

	'admin' => [],

	'client' => [
		'html' => [
			'basket' => [
				'cache' => [
					// 'enable' => false, // Disable basket content caching for development
				],
			],
			'common' => [
				'cache' => [
					// 'force' => true // enforce caching for logged in users
				],
			],
			'catalog' => [
				'lists' => [
					'basket-add' => true, // shows add to basket in list views
					// 'infinite-scroll' => true, // load more products in list view
					// 'size' => 48, // number of products per page
				],
				'selection' => [
					'type' => [// how variant attributes are displayed
						'color' => 'radio',
						'length' => 'radio',
						'width' => 'radio',
					],
				],
			],
		],
	],

	'controller' => [
		'frontend' => [
			'catalog' => [
				'levels-always' => 3 // number of category levels for mega menu
			]
		]
	],

	'i18n' => [
	],

	'madmin' => [
		'cache' => [
			'manager' => [
				// 'name' => 'None', // Disable caching for development
			],
		],
		'log' => [
			'manager' => [
				// 'loglevel' => 7, // Enable debug logging into madmin_log table
			],
		],
	],

	'mshop' => [
		'locale' => [
			// 'site' => '<custom site code>', // used instead of "default"
		]
	],


	'command' => [
	],

	'frontend' => [
	],

	'backend' => [
	],

];


================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="vendor/autoload.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd" cacheDirectory=".phpunit.cache">
  <source>
    <include>
      <directory suffix=".php">./src</directory>
    </include>
  </source>
  <coverage>
    <report>
      <clover outputFile="coverage.xml"/>
    </report>
  </coverage>
  <php>
    <env name="APP_ENV" value="testing"/>
    <env name="CACHE_DRIVER" value="array"/>
    <env name="SESSION_DRIVER" value="array"/>
    <env name="XDEBUG_MODE" value="coverage"/>
  </php>
  <testsuites>
    <testsuite name="command">
      <file>./tests/Command/SetupCommandTest.php</file>
      <file>./tests/Command/ClearCommandTest.php</file>
      <file>./tests/Command/JobsCommandTest.php</file>
      <file>./tests/Command/AccountCommandTest.php</file>
    </testsuite>
    <testsuite name="base">
      <directory>./tests/Base</directory>
    </testsuite>
    <testsuite name="controller">
      <directory>./tests/Controller</directory>
    </testsuite>
    <testsuite name="helpers">
      <file>./tests/HelpersTest.php</file>
    </testsuite>
  </testsuites>
</phpunit>


================================================
FILE: routes/aimeos.php
================================================
<?php

if( ( $conf = config( 'shop.routes.admin', ['prefix' => 'admin', 'middleware' => ['web']] ) ) !== false ) {

	Route::group( $conf, function() {

		Route::match( array( 'GET' ), '', array(
			'as' => 'aimeos_shop_admin',
			'uses' => 'Aimeos\Shop\Controller\AdminController@indexAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

	});
}


if( ( $conf = config( 'shop.routes.jqadm', ['prefix' => 'admin/{site}/jqadm', 'middleware' => ['web', 'auth']] ) ) !== false ) {

	Route::group( $conf, function() {

		Route::match( array( 'GET' ), 'file/{name}/{locale}', array(
			'as' => 'aimeos_shop_jqadm_file',
			'uses' => 'Aimeos\Shop\Controller\JqadmController@fileAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

		Route::match( array( 'POST' ), 'batch/{resource}', array(
			'as' => 'aimeos_shop_jqadm_batch',
			'uses' => 'Aimeos\Shop\Controller\JqadmController@batchAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

		Route::match( array( 'GET', 'POST' ), 'copy/{resource}/{id}', array(
			'as' => 'aimeos_shop_jqadm_copy',
			'uses' => 'Aimeos\Shop\Controller\JqadmController@copyAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

		Route::match( array( 'GET', 'POST' ), 'create/{resource}', array(
			'as' => 'aimeos_shop_jqadm_create',
			'uses' => 'Aimeos\Shop\Controller\JqadmController@createAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

		Route::match( array( 'POST' ), 'delete/{resource}/{id?}', array(
			'as' => 'aimeos_shop_jqadm_delete',
			'uses' => 'Aimeos\Shop\Controller\JqadmController@deleteAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

		Route::match( array( 'GET', 'POST' ), 'export/{resource}', array(
			'as' => 'aimeos_shop_jqadm_export',
			'uses' => 'Aimeos\Shop\Controller\JqadmController@exportAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

		Route::match( array( 'GET' ), 'get/{resource}/{id}', array(
			'as' => 'aimeos_shop_jqadm_get',
			'uses' => 'Aimeos\Shop\Controller\JqadmController@getAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

		Route::match( array( 'POST' ), 'import/{resource}', array(
			'as' => 'aimeos_shop_jqadm_import',
			'uses' => 'Aimeos\Shop\Controller\JqadmController@importAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

		Route::match( array( 'POST' ), 'save/{resource}', array(
			'as' => 'aimeos_shop_jqadm_save',
			'uses' => 'Aimeos\Shop\Controller\JqadmController@saveAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

		Route::match( array( 'GET', 'POST' ), 'search/{resource}', array(
			'as' => 'aimeos_shop_jqadm_search',
			'uses' => 'Aimeos\Shop\Controller\JqadmController@searchAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

	});
}


if( ( $conf = config( 'shop.routes.graphql', ['prefix' => 'admin/{site}/graphql', 'middleware' => ['web', 'auth']] ) ) !== false ) {

	Route::group( $conf, function() {

		Route::match( array( 'POST' ), '', array(
			'as' => 'aimeos_shop_graphql_post',
			'uses' => 'Aimeos\Shop\Controller\GraphqlController@indexAction'
		) )->where( ['site' => '[A-Za-z0-9\.\-]+'] );

	});
}


if( ( $conf = config( 'shop.routes.jsonadm', ['prefix' => 'admin/{site}/jsonadm', 'middleware' => ['web', 'auth']] ) ) !== false ) {

	Route::group( $conf, function() {

		Route::match( array( 'DELETE' ), '{resource}/{id?}', array(
			'as' => 'aimeos_shop_jsonadm_delete',
			'uses' => 'Aimeos\Shop\Controller\JsonadmController@deleteAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

		Route::match( array( 'GET' ), '{resource}/{id?}', array(
			'as' => 'aimeos_shop_jsonadm_get',
			'uses' => 'Aimeos\Shop\Controller\JsonadmController@getAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

		Route::match( array( 'PATCH' ), '{resource}/{id?}', array(
			'as' => 'aimeos_shop_jsonadm_patch',
			'uses' => 'Aimeos\Shop\Controller\JsonadmController@patchAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

		Route::match( array( 'POST' ), '{resource}/{id?}', array(
			'as' => 'aimeos_shop_jsonadm_post',
			'uses' => 'Aimeos\Shop\Controller\JsonadmController@postAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

		Route::match( array( 'PUT' ), '{resource}/{id?}', array(
			'as' => 'aimeos_shop_jsonadm_put',
			'uses' => 'Aimeos\Shop\Controller\JsonadmController@putAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

		Route::match( array( 'OPTIONS' ), '{resource?}', array(
			'as' => 'aimeos_shop_jsonadm_options',
			'uses' => 'Aimeos\Shop\Controller\JsonadmController@optionsAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'resource' => '[a-z\/]+'] );

	});
}


if( ( $conf = config( 'shop.routes.jsonapi', ['prefix' => 'jsonapi', 'middleware' => ['web', 'api']] ) ) !== false ) {

	Route::group( $conf, function() {

		Route::match( array( 'DELETE' ), '{resource}', array(
			'as' => 'aimeos_shop_jsonapi_delete',
			'uses' => 'Aimeos\Shop\Controller\JsonapiController@deleteAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

		Route::match( array( 'GET' ), '{resource}', array(
			'as' => 'aimeos_shop_jsonapi_get',
			'uses' => 'Aimeos\Shop\Controller\JsonapiController@getAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

		Route::match( array( 'PATCH' ), '{resource}', array(
			'as' => 'aimeos_shop_jsonapi_patch',
			'uses' => 'Aimeos\Shop\Controller\JsonapiController@patchAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

		Route::match( array( 'POST' ), '{resource}', array(
			'as' => 'aimeos_shop_jsonapi_post',
			'uses' => 'Aimeos\Shop\Controller\JsonapiController@postAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

		Route::match( array( 'PUT' ), '{resource}', array(
			'as' => 'aimeos_shop_jsonapi_put',
			'uses' => 'Aimeos\Shop\Controller\JsonapiController@putAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

		Route::match( array( 'GET', 'OPTIONS' ), '{resource?}', array(
			'as' => 'aimeos_shop_jsonapi_options',
			'uses' => 'Aimeos\Shop\Controller\JsonapiController@optionsAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

	});
}


if( ( $conf = config( 'shop.routes.account', ['prefix' => 'profile', 'middleware' => ['web', 'auth']] ) ) !== false ) {

	Route::group( $conf, function() {

		Route::match( array( 'GET', 'POST' ), 'favorite/{fav_action?}/{fav_id?}/{d_name?}/{d_pos?}', array(
			'as' => 'aimeos_shop_account_favorite',
			'uses' => 'Aimeos\Shop\Controller\AccountController@indexAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

		Route::match( array( 'GET', 'POST' ), 'watch/{wat_action?}/{wat_id?}/{d_name?}/{d_pos?}', array(
			'as' => 'aimeos_shop_account_watch',
			'uses' => 'Aimeos\Shop\Controller\AccountController@indexAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

		Route::match( array( 'GET', 'POST' ), 'download/{dl_id}', array(
			'as' => 'aimeos_shop_account_download',
			'uses' => 'Aimeos\Shop\Controller\AccountController@downloadAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

		Route::match( array( 'GET', 'POST' ), '', array(
			'as' => 'aimeos_shop_account',
			'uses' => 'Aimeos\Shop\Controller\AccountController@indexAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

	});
}


if( ( $conf = config( 'shop.routes.supplier', ['prefix' => 'brand', 'middleware' => ['web']] ) ) !== false ) {

	Route::group( $conf, function() {

		Route::match( array( 'GET', 'POST' ), '{s_name}/{f_supid}', array(
			'as' => 'aimeos_shop_supplier',
			'uses' => 'Aimeos\Shop\Controller\SupplierController@detailAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

	} );
}


if( ( $conf = config( 'shop.routes.update', [] ) ) !== false ) {

	Route::group( $conf, function() {

		Route::match( array( 'GET', 'POST' ), 'update', array(
			'as' => 'aimeos_shop_update',
			'uses' => 'Aimeos\Shop\Controller\CheckoutController@updateAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

	});
}


if( ( $conf = config( 'shop.routes.confirm', ['prefix' => 'shop', 'middleware' => ['web']] ) ) !== false ) {

	Route::group( $conf, function() {

		Route::match( array( 'GET', 'POST' ), 'confirm/{code?}', array(
			'as' => 'aimeos_shop_confirm',
			'uses' => 'Aimeos\Shop\Controller\CheckoutController@confirmAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

	});
}


if( ( $conf = config( 'shop.routes.checkout', ['prefix' => 'shop', 'middleware' => ['web']] ) ) !== false ) {

	Route::group( $conf, function() {

		Route::match( array( 'GET', 'POST' ), 'checkout/{c_step?}', array(
			'as' => 'aimeos_shop_checkout',
			'uses' => 'Aimeos\Shop\Controller\CheckoutController@indexAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

	});
}


if( ( $conf = config( 'shop.routes.basket', ['prefix' => 'shop', 'middleware' => ['web']] ) ) !== false ) {

	Route::group( $conf, function() {

		Route::match( array( 'GET', 'POST' ), 'basket', array(
			'as' => 'aimeos_shop_basket',
			'uses' => 'Aimeos\Shop\Controller\BasketController@indexAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

	});
}


if( ( $conf = config( 'shop.routes.default', ['prefix' => 'shop', 'middleware' => ['web']] ) ) !== false ) {

	Route::group( $conf, function() {

		Route::match( array( 'GET', 'POST' ), 'count', array(
			'as' => 'aimeos_shop_count',
			'uses' => 'Aimeos\Shop\Controller\CatalogController@countAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

		Route::match( array( 'GET', 'POST' ), 'suggest', array(
			'as' => 'aimeos_shop_suggest',
			'uses' => 'Aimeos\Shop\Controller\CatalogController@suggestAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

		Route::match( array( 'GET', 'POST' ), 'stock', array(
			'as' => 'aimeos_shop_stock',
			'uses' => 'Aimeos\Shop\Controller\CatalogController@stockAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

		Route::match( array( 'GET', 'POST' ), 'pin', array(
			'as' => 'aimeos_shop_session_pinned',
			'uses' => 'Aimeos\Shop\Controller\CatalogController@sessionAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

		Route::match( array( 'GET', 'POST' ), 'search', array(
			'as' => 'aimeos_shop_list',
			'uses' => 'Aimeos\Shop\Controller\CatalogController@listAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

		Route::match( array( 'GET', 'POST' ), '{f_name}~{f_catid}/{l_page?}', array(
			'as' => 'aimeos_shop_tree',
			'uses' => 'Aimeos\Shop\Controller\CatalogController@treeAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'f_name' => '[^~]*', 'l_page' => '[0-9]+'] );

		Route::match( array( 'GET', 'POST' ), '{d_name}/{d_pos?}/{d_prodid?}', array(
			'as' => 'aimeos_shop_detail',
			'uses' => 'Aimeos\Shop\Controller\CatalogController@detailAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'd_pos' => '[0-9]*'] );

	});
}


if( ( $conf = config( 'shop.routes.page', ['prefix' => 'p', 'middleware' => ['web']] ) ) !== false ) {

	Route::group( $conf, function() {

		Route::match(['GET', 'POST'], '{path?}', [
			'as' => 'aimeos_page',
			'uses' => '\Aimeos\Shop\Controller\PageController@indexAction'
		] )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );
	});
}


if( ( $conf = config( 'shop.routes.home', ['middleware' => ['web']] ) ) !== false ) {

	Route::group( $conf, function() {

		Route::match( array( 'GET', 'POST' ), '/', array(
			'as' => 'aimeos_home',
			'uses' => 'Aimeos\Shop\Controller\CatalogController@homeAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+'] );

	});
}


================================================
FILE: src/Base/Aimeos.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */

namespace Aimeos\Shop\Base;


/**
 * Service providing the Aimeos object
 */
class Aimeos
{
	/**
	 * @var \Illuminate\Contracts\Config\Repository
	 */
	private $config;

	/**
	 * @var \Aimeos\Bootstrap
	 */
	private $object;


	/**
	 * Initializes the object
	 *
	 * @param \Illuminate\Contracts\Config\Repository $config Configuration object
	 */
	public function __construct( \Illuminate\Contracts\Config\Repository $config )
	{
		$this->config = $config;
	}


	/**
	 * Returns the Aimeos object.
	 *
	 * @return \Aimeos\Bootstrap Aimeos bootstrap object
	 */
	public function get() : \Aimeos\Bootstrap
	{
		if( $this->object === null )
		{
			$dir = base_path( 'ext' );

			if( !is_dir( $dir ) ) {
				$dir = dirname( __DIR__, 4 ) . DIRECTORY_SEPARATOR . 'ext';
			}

			$extDirs = (array) $this->config->get( 'shop.extdir', $dir );
			$this->object = new \Aimeos\Bootstrap( $extDirs, false );
		}

		return $this->object;
	}


	/**
	 * Returns the version of the Aimeos package
	 *
	 * @return string Version string
	 */
	public function getVersion() : string
	{
		if( ( $content = @file_get_contents( base_path( 'composer.lock' ) ) ) !== false
			&& ( $content = json_decode( $content, true ) ) !== null && isset( $content['packages'] )
		) {
			foreach( (array) $content['packages'] as $item )
			{
				if( $item['name'] === 'aimeos/aimeos-laravel' ) {
					return $item['version'];
				}
			}
		}

		return '';
	}
}

================================================
FILE: src/Base/Config.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */

namespace Aimeos\Shop\Base;


/**
 * Service providing the config object
 */
class Config
{
	/**
	 * @var \Aimeos\Shop\Base\Config[]
	 */
	private $objects = [];

	/**
	 * @var \Aimeos\Shop\Base\Aimeos
	 */
	private $aimeos;

	/**
	 * @var \Illuminate\Contracts\Config\Repository
	 */
	private $config;


	/**
	 * Initializes the object
	 *
	 * @param \Illuminate\Contracts\Config\Repository $config Configuration object
	 * @param \Aimeos\Shop\Base\Aimeos $aimeos Aimeos object
	 */
	public function __construct( \Illuminate\Contracts\Config\Repository $config, \Aimeos\Shop\Base\Aimeos $aimeos )
	{
		$this->aimeos = $aimeos;
		$this->config = $config;
	}


	/**
	 * Creates a new configuration object.
	 *
	 * @param string $type Configuration type ("frontend" or "backend")
	 * @return \Aimeos\Base\Config\Iface Configuration object
	 */
	public function get( string $type = 'frontend' ) : \Aimeos\Base\Config\Iface
	{
		if( !isset( $this->objects[$type] ) )
		{
			$configPaths = $this->aimeos->get()->getConfigPaths();
			$cfgfile = dirname( dirname( __DIR__ ) ) . '/config/default.php';

			$config = new \Aimeos\Base\Config\PHPArray( require $cfgfile, $configPaths );

			if( $this->config->get( 'shop.apc_enabled', false ) == true ) {
				$config = new \Aimeos\Base\Config\Decorator\APC( $config, $this->config->get( 'shop.apc_prefix', 'laravel:' ) );
			}

			$config = new \Aimeos\Base\Config\Decorator\Memory( $config, $this->config->get( 'shop' ) );

			if( ( $conf = $this->config->get( 'shop.' . $type, [] ) ) !== [] ) {
				$config = new \Aimeos\Base\Config\Decorator\Memory( $config, $conf );
			}

			$this->objects[$type] = $config;
		}

		return $this->objects[$type];
	}
}


================================================
FILE: src/Base/Context.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */

namespace Aimeos\Shop\Base;


use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Session;


/**
 * Service providing the context objects
 */
class Context
{
	/**
	 * @var \Aimeos\MShop\ContextIface
	 */
	private $context;

	/**
	 * @var \Aimeos\Shop\Base\Config
	 */
	private $config;

	/**
	 * @var \Aimeos\Shop\Base\I18n
	 */
	private $i18n;

	/**
	 * @var \Aimeos\Shop\Base\Locale
	 */
	private $locale;

	/**
	 * @var \Illuminate\Session\Store
	 */
	private $session;


	/**
	 * Initializes the object
	 *
	 * @param \Illuminate\Session\Store $session Laravel session object
	 * @param \Aimeos\Shop\Base\Config $config Configuration object
	 * @param \Aimeos\Shop\Base\Locale $locale Locale object
	 * @param \Aimeos\Shop\Base\I18n $i18n Internationalisation object
	 */
	public function __construct( \Illuminate\Session\Store $session, \Aimeos\Shop\Base\Config $config, \Aimeos\Shop\Base\Locale $locale, \Aimeos\Shop\Base\I18n $i18n )
	{
		$this->session = $session;
		$this->config = $config;
		$this->locale = $locale;
		$this->i18n = $i18n;
	}


	/**
	 * Returns the current context
	 *
	 * @param bool $locale True to add locale object to context, false if not (deprecated, use \Aimeos\Shop\Base\Locale)
	 * @param string $type Configuration type, i.e. "frontend" or "backend" (deprecated, use \Aimeos\Shop\Base\Config)
	 * @return \Aimeos\MShop\ContextIface Context object
	 */
	public function get( bool $locale = true, string $type = 'frontend' ) : \Aimeos\MShop\ContextIface
	{
		$config = $this->config->get( $type );

		if( $this->context === null )
		{
			$context = new \Aimeos\MShop\Context();
			$context->setConfig( $config );

			$this->addDataBaseManager( $context );
			$this->addFilesystemManager( $context );
			$this->addMessageQueueManager( $context );
			$this->addLogger( $context );
			$this->addCache( $context );
			$this->addMailer( $context );
			$this->addNonce( $context );
			$this->addPassword( $context );
			$this->addProcess( $context );
			$this->addSession( $context );
			$this->addToken( $context );
			$this->addUserGroups( $context );

			$this->context = $context;
		}

		$this->context->setConfig( $config );

		if( $locale === true )
		{
			$localeItem = $this->locale->get( $this->context );
			$this->context->setLocale( $localeItem );
			$this->context->setI18n( $this->i18n->get( array( $localeItem->getLanguageId() ) ) );

			$config->apply( $localeItem->getSiteItem()->getConfig() );
		}

		return $this->context;
	}


	/**
	 * Adds the cache object to the context
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object including config
	 * @return \Aimeos\MShop\ContextIface Modified context object
	 */
	protected function addCache( \Aimeos\MShop\ContextIface $context ) : \Aimeos\MShop\ContextIface
	{
		$cache = \Aimeos\MAdmin::create( $context, 'cache' )->getCache();

		return $context->setCache( $cache );
	}


	/**
	 * Adds the database manager object to the context
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @return \Aimeos\MShop\ContextIface Modified context object
	 */
	protected function addDatabaseManager( \Aimeos\MShop\ContextIface $context ) : \Aimeos\MShop\ContextIface
	{
		$dbm = new \Aimeos\Base\DB\Manager\Standard( $context->config()->get( 'resource' ), 'DBAL' );

		return $context->setDatabaseManager( $dbm );
	}


	/**
	 * Adds the filesystem manager object to the context
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @return \Aimeos\MShop\ContextIface Modified context object
	 */
	protected function addFilesystemManager( \Aimeos\MShop\ContextIface $context ) : \Aimeos\MShop\ContextIface
	{
		$config = $context->config()->get( 'resource' );
		$fs = new \Aimeos\Base\Filesystem\Manager\Laravel( app( 'filesystem' ), $config, storage_path( 'aimeos' ) );

		return $context->setFilesystemManager( $fs );
	}


	/**
	 * Adds the logger object to the context
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @return \Aimeos\MShop\ContextIface Modified context object
	 */
	protected function addLogger( \Aimeos\MShop\ContextIface $context ) : \Aimeos\MShop\ContextIface
	{
		$logger = \Aimeos\MAdmin::create( $context, 'log' );

		return $context->setLogger( $logger );
	}



	/**
	 * Adds the mailer object to the context
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @return \Aimeos\MShop\ContextIface Modified context object
	 */
	protected function addMailer( \Aimeos\MShop\ContextIface $context ) : \Aimeos\MShop\ContextIface
	{
		$mail = new \Aimeos\Base\Mail\Manager\Laravel( app( 'mail.manager' ) );

		return $context->setMail( $mail );
	}


	/**
	 * Adds the message queue manager object to the context
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @return \Aimeos\MShop\ContextIface Modified context object
	 */
	protected function addMessageQueueManager( \Aimeos\MShop\ContextIface $context ) : \Aimeos\MShop\ContextIface
	{
		$mq = new \Aimeos\Base\MQueue\Manager\Standard( $context->config()->get( 'resource' ) );

		return $context->setMessageQueueManager( $mq );
	}


	/**
	 * Adds the nonce value for inline JS to the context
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @return \Aimeos\MShop\ContextIface Modified context object
	 */
	protected function addNonce( \Aimeos\MShop\ContextIface $context ) : \Aimeos\MShop\ContextIface
	{
		return $context->setNonce( base64_encode( random_bytes( 16 ) ) );
	}


	/**
	 * Adds the password hasher object to the context
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @return \Aimeos\MShop\ContextIface Modified context object
	 */
	protected function addPassword( \Aimeos\MShop\ContextIface $context ) : \Aimeos\MShop\ContextIface
	{
		return $context->setPassword( new \Aimeos\Base\Password\Standard() );
	}


	/**
	 * Adds the process object to the context
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @return \Aimeos\MShop\ContextIface Modified context object
	 */
	protected function addProcess( \Aimeos\MShop\ContextIface $context ) : \Aimeos\MShop\ContextIface
	{
		$config = $context->config();
		$max = $config->get( 'pcntl_max', 4 );
		$prio = $config->get( 'pcntl_priority', 19 );

		$process = new \Aimeos\Base\Process\Pcntl( $max, $prio );
		$process = new \Aimeos\Base\Process\Decorator\Check( $process );

		return $context->setProcess( $process );
	}


	/**
	 * Adds the session object to the context
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @return \Aimeos\MShop\ContextIface Modified context object
	 */
	protected function addSession( \Aimeos\MShop\ContextIface $context ) : \Aimeos\MShop\ContextIface
	{
		$session = new \Aimeos\Base\Session\Laravel( $this->session );

		return $context->setSession( $session );
	}


	/**
	 * Adds the session token to the context
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @return \Aimeos\MShop\ContextIface Modified context object
	 */
	protected function addToken( \Aimeos\MShop\ContextIface $context ) : \Aimeos\MShop\ContextIface
	{
		if( ( $token = Session::get( 'token' ) ) === null ) {
			Session::put( 'token', $token = Session::getId() );
		}

		return $context->setToken( $token );
	}


	/**
	 * Adds the user and groups if available
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @return \Aimeos\MShop\ContextIface Modified context object
	 */
	protected function addUserGroups( \Aimeos\MShop\ContextIface $context ) : \Aimeos\MShop\ContextIface
	{
		$key = collect( config( 'shop.routes' ) )
			->where( 'prefix', optional( Route::getCurrentRoute() )->getPrefix() )
			->keys()->first();
		$gname = data_get( config( 'shop.guards' ), $key, Auth::getDefaultDriver() );

		if( ( $guard = Auth::guard( $gname ) ) && ( $userid = $guard->id() ) )
		{
			$context->setUser( function() use ( $context, $userid ) {
				try {
					return \Aimeos\MShop::create( $context, 'customer' )->get( $userid, ['group'] );
				} catch( \Aimeos\MShop\Exception $e ) { // avoid errors if user is assigned to another site
					return null;
				}
			} );

			$context->setGroups( function() use ( $context ) {
				return $context->user()?->getGroups() ?? [];
			} );

			$context->setEditor( $guard->user()?->email ?: \Request::ip() );
		}
		elseif( $ip = \Request::ip() )
		{
			$context->setEditor( $ip );
		}

		return $context;
	}
}


================================================
FILE: src/Base/I18n.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */

namespace Aimeos\Shop\Base;


/**
 * Service providing the internationalization objects
 */
class I18n
{
	/**
	 * @var \Aimeos\Shop\Base\Aimeos
	 */
	private $aimeos;

	/**
	 * @var \Illuminate\Contracts\Config\Repository
	 */
	private $config;

	/**
	 * @var array
	 */
	private $i18n = [];


	/**
	 * Initializes the object
	 *
	 * @param \Illuminate\Contracts\Config\Repository $config Configuration object
	 * @param \Aimeos\Shop\Base\Aimeos $aimeos Aimeos object
	 */
	public function __construct( \Illuminate\Contracts\Config\Repository $config, \Aimeos\Shop\Base\Aimeos $aimeos )
	{
		$this->aimeos = $aimeos;
		$this->config = $config;
	}


	/**
	 * Creates new translation objects.
	 *
	 * @param array $languageIds List of two letter ISO language IDs
	 * @return \Aimeos\Base\Translation\Iface[] List of translation objects
	 */
	public function get( array $languageIds ) : array
	{
		$i18nPaths = $this->aimeos->get()->getI18nPaths();

		foreach( $languageIds as $langid )
		{
			if( !isset( $this->i18n[$langid] ) )
			{
				$i18n = new \Aimeos\Base\Translation\Gettext( $i18nPaths, $langid );

				if( $this->config->get( 'shop.apc_enabled', false ) == true ) {
					$i18n = new \Aimeos\Base\Translation\Decorator\APC( $i18n, $this->config->get( 'shop.apc_prefix', 'laravel:' ) );
				}

				if( $this->config->has( 'shop.i18n.' . $langid ) ) {
					$i18n = new \Aimeos\Base\Translation\Decorator\Memory( $i18n, $this->config->get( 'shop.i18n.' . $langid ) );
				}

				$this->i18n[$langid] = $i18n;
			}
		}

		return $this->i18n;
	}
}

================================================
FILE: src/Base/Locale.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */

namespace Aimeos\Shop\Base;


use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Route;


/**
 * Service providing the context objects
 */
class Locale
{
	/**
	 * @var \Illuminate\Contracts\Config\Repository
	 */
	private $config;

	/**
	 * @var \Aimeos\MShop\Locale\Item\Iface
	 */
	private $locale;


	/**
	 * Initializes the object
	 *
	 * @param \Illuminate\Contracts\Config\Repository $config Configuration object
	 */
	public function __construct( \Illuminate\Contracts\Config\Repository $config )
	{
		$this->config = $config;
	}


	/**
	 * Returns the locale item for the current request
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @return \Aimeos\MShop\Locale\Item\Iface Locale item object
	 */
	public function get( \Aimeos\MShop\ContextIface $context ) : \Aimeos\MShop\Locale\Item\Iface
	{
		if( $this->locale === null )
		{
			$site = config( 'shop.mshop.locale.site', 'default' );
			$lang = app()->getLocale();
			$currency = '';

			if( Route::current() )
			{
				$site = Request::route( 'site', $site );
				$lang = Request::route( 'locale', $lang );
				$currency = Request::route( 'currency', $currency );
			}

			$site = Request::input( 'site', $site );
			$lang = Request::input( 'locale', $lang );
			$currency = Request::input( 'currency', $currency );

			$localeManager = \Aimeos\MShop::create( $context, 'locale' );
			$disableSites = $this->config->get( 'shop.disableSites', true );

			$this->locale = $localeManager->bootstrap( $site, $lang, $currency, $disableSites );
		}

		return $this->locale;
	}


	/**
	 * Returns the locale item for the current request
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @param string $site Unique site code
	 * @return \Aimeos\MShop\Locale\Item\Iface Locale item object
	 */
	public function getBackend( \Aimeos\MShop\ContextIface $context, string $site ) : \Aimeos\MShop\Locale\Item\Iface
	{
		$localeManager = \Aimeos\MShop::create( $context, 'locale' );

		try {
			$localeItem = $localeManager->bootstrap( $site, '', '', false, null, true );
		} catch( \Aimeos\MShop\Exception $e ) {
			$localeItem = $localeManager->create();
		}

		return $localeItem->setCurrencyId( null )->setLanguageId( null );
	}
}


================================================
FILE: src/Base/Shop.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */

namespace Aimeos\Shop\Base;


/**
 * Service providing the shop object
 */
class Shop
{
	/**
	 * @var \Aimeos\MShop\ContextIface
	 */
	private $context;

	/**
	 * @var \Aimeos\Base\View\Iface
	 */
	private $view;

	/**
	 * @var array
	 */
	private $objects = [];


	/**
	 * Initializes the object
	 *
	 * @param \Aimeos\Shop\Base\Aimeos $aimeos Aimeos object
	 * @param \Aimeos\Shop\Base\Context $context Context object
	 * @param \Aimeos\Shop\Base\View $view View object
	 */
	public function __construct( \Aimeos\Shop\Base\Aimeos $aimeos,
		\Aimeos\Shop\Base\Context $context, \Aimeos\Shop\Base\View $view )
	{
		$this->context = $context->get();
		$locale = $this->context->locale();

		$tmplPaths = $aimeos->get()->getTemplatePaths( 'client/html/templates', $locale->getSiteItem()->getTheme() );
		$langid = $locale->getLanguageId();

		$this->view = $view->create( $this->context, $tmplPaths, $langid );
		$this->context->setView( $this->view );
	}


	/**
	 * Returns the HTML client for the given name
	 *
	 * @param string $name Name of the shop component
	 * @return \Aimeos\Client\Html\Iface HTML client
	 */
	public function get( string $name ) : \Aimeos\Client\Html\Iface
	{
		if( !isset( $this->objects[$name] ) )
		{
			$client = \Aimeos\Client\Html::create( $this->context, $name );
			$client->setView( clone $this->view );
			$client->init();

			$this->objects[$name] = $client;
		}

		return $this->objects[$name];
	}


	/** Returns the view template for the given name
	 *
	 * @param string $name View name, e.g. "account.index"
	 * @return string Template name, e.g. "shop::account.indx"
	 */
	public function template( string $name ) : string
	{
		$theme = $this->context->locale()->getSiteItem()->getTheme();
		return \Illuminate\Support\Facades\View::exists( $theme . '::' . $name ) ? $theme . '::' . $name : 'shop::' . $name;
	}


	/**
	 * Returns the used view object
	 *
	 * @return \Aimeos\Base\View\Iface View object
	 */
	public function view() : \Aimeos\Base\View\Iface
	{
		return $this->view;
	}
}


================================================
FILE: src/Base/Support.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */

namespace Aimeos\Shop\Base;


use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Route;


/**
 * Service providing the supporting functionality
 */
class Support
{
	/**
	 * @var \Aimeos\Shop\Base\Context
	 */
	private $context;

	/**
	 * @var \Aimeos\Shop\Base\Locale
	 */
	private $locale;

	/**
	 * @var array
	 */
	private $access = [];


	/**
	 * Initializes the object
	 *
	 * @param \Aimeos\Shop\Base\Context $context Context provider
	 * @param \Aimeos\Shop\Base\Locale $locale Locale provider
	 */
	public function __construct( \Aimeos\Shop\Base\Context $context, \Aimeos\Shop\Base\Locale $locale )
	{
		$this->context = $context;
		$this->locale = $locale;
	}


	/**
	 * Checks if the user is in the specified group and associatied to the site
	 *
	 * @param \Illuminate\Foundation\Auth\User $user Authenticated user
	 * @param string|array $groupcodes Unique user/customer group codes that are allowed
	 * @return bool True if user is part of the group, false if not
	 */
	public function checkUserGroup( \Illuminate\Foundation\Auth\User $user, $groupcodes ) : bool
	{
		$groups = ( is_array( $groupcodes ) ? implode( ',', $groupcodes ) : $groupcodes );

		if( isset( $this->access[$user->id][$groups] ) ) {
			return $this->access[$user->id][$groups];
		}

		$this->access[$user->id][$groups] = false;

		$context = $this->context->get( false );
		$siteid = current( array_reverse( explode( '.', trim( $user->siteid, '.' ) ) ) );

		if( $siteid ) {
			$site = \Aimeos\MShop::create( $context, 'locale/site' )->get( $siteid )->getCode();
		} else {
			$site = config( 'shop.mshop.locale.site', 'default' );
		}

		$site = ( Route::current() ? Route::input( 'site', Request::get( 'site', $site ) ) : $site );
		$context->setLocale( $this->locale->getBackend( $context, $site ) );

		foreach( array_reverse( $context->locale()->getSitePath() ) as $siteid )
		{
			if( $user->siteid === '' || $user->siteid === $siteid ) {
				$this->access[$user->id][$groups] = $this->checkGroups( $context, $user->id, $groupcodes );
			}
		}

		return $this->access[$user->id][$groups];
	}


	/**
	 * Checks if one of the groups is associated to the given user ID
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context item
	 * @param string $userid ID of the logged in user
	 * @param string[]|string $groupcodes List of group codes to check against
	 * @return bool True if the user is in one of the groups, false if not
	 */
	protected function checkGroups( \Aimeos\MShop\ContextIface $context, string $userid, $groupcodes ) : bool
	{
		$manager = \Aimeos\MShop::create( $context, 'group' );

		$search = $manager->filter();
		$search->setConditions( $search->compare( '==', 'group.code', (array) $groupcodes ) );
		$groupIds = $manager->search( $search )->keys()->toArray();

		$manager = \Aimeos\MShop::create( $context, 'customer/lists' );

		$search = $manager->filter()->slice( 0, 1 );
		$expr = array(
			$search->compare( '==', 'customer.lists.parentid', $userid ),
			$search->compare( '==', 'customer.lists.refid', $groupIds ),
			$search->compare( '==', 'customer.lists.domain', 'group' ),
		);
		$search->setConditions( $search->combine( '&&', $expr ) );

		return !$manager->search( $search )->isEmpty();
	}
}


================================================
FILE: src/Base/View.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */

namespace Aimeos\Shop\Base;


use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Response;


/**
 * Service providing the view objects
 */
class View
{
	/**
	 * @var \Illuminate\Contracts\Config\Repository
	 */
	private $config;

	/**
	 * @var \Aimeos\Shop\Base\I18n
	 */
	private $i18n;

	/**
	 * @var \Aimeos\Shop\Base\Support
	 */
	private $support;


	/**
	 * Initializes the object
	 *
	 * @param \Illuminate\Contracts\Config\Repository $config Configuration object
	 * @param \Aimeos\Shop\Base\I18n $i18n I18n object
	 * @param \Aimeos\Shop\Base\Support $support Support object
	 */
	public function __construct( \Illuminate\Contracts\Config\Repository $config,
		\Aimeos\Shop\Base\I18n $i18n, \Aimeos\Shop\Base\Support $support )
	{
		$this->i18n = $i18n;
		$this->config = $config;
		$this->support = $support;
	}


	/**
	 * Creates the view object for the HTML client.
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @param array $templatePaths List of base path names with relative template paths as key/value pairs
	 * @param string|null $locale Code of the current language or null for no translation
	 * @return \Aimeos\Base\View\Iface View object
	 */
	public function create( \Aimeos\MShop\ContextIface $context, array $templatePaths,
		string $locale = null ) : \Aimeos\Base\View\Iface
	{
		$engine = new \Aimeos\Base\View\Engine\Blade( app( 'Illuminate\Contracts\View\Factory' ) );
		$view = new \Aimeos\Base\View\Standard( $templatePaths, array( '.blade.php' => $engine ) );

		$config = $context->config();
		$session = $context->session();

		$this->addCsrf( $view );
		$this->addAccess( $view, $context );
		$this->addConfig( $view, $config );
		$this->addNumber( $view, $config, $locale );
		$this->addParam( $view );
		$this->addRequest( $view );
		$this->addResponse( $view );
		$this->addSession( $view, $session );
		$this->addTranslate( $view, $locale );
		$this->addUrl( $view );

		return $view;
	}


	/**
	 * Adds the "access" helper to the view object
	 *
	 * @param \Aimeos\Base\View\Iface $view View object
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @return \Aimeos\Base\View\Iface Modified view object
	 */
	protected function addAccess( \Aimeos\Base\View\Iface $view, \Aimeos\MShop\ContextIface $context ) : \Aimeos\Base\View\Iface
	{
		if( $this->config->get( 'shop.accessControl', true ) === false
			|| ( ( $user = \Illuminate\Support\Facades\Auth::user() ) !== null && $user->superuser )
		) {
			$helper = new \Aimeos\Base\View\Helper\Access\All( $view );
		}
		else
		{
			$helper = new \Aimeos\Base\View\Helper\Access\Standard( $view, function() use ( $context ) {
				$manager = \Aimeos\MShop::create( $context, 'group' );
				$filter = $manager->filter( true )->add( 'group.id', '==', $context->groups() );
				return $manager->search( $filter )->col( 'group.code' )->all();
			} );
		}

		$view->addHelper( 'access', $helper );

		return $view;
	}


	/**
	 * Adds the "config" helper to the view object
	 *
	 * @param \Aimeos\Base\View\Iface $view View object
	 * @param \Aimeos\Base\Config\Iface $config Configuration object
	 * @return \Aimeos\Base\View\Iface Modified view object
	 */
	protected function addConfig( \Aimeos\Base\View\Iface $view, \Aimeos\Base\Config\Iface $config ) : \Aimeos\Base\View\Iface
	{
		$config = new \Aimeos\Base\Config\Decorator\Protect( clone $config, ['resource/*/baseurl'], ['resource'] );
		$helper = new \Aimeos\Base\View\Helper\Config\Standard( $view, $config );
		$view->addHelper( 'config', $helper );

		return $view;
	}


	/**
	 * Adds the "access" helper to the view object
	 *
	 * @param \Aimeos\Base\View\Iface $view View object
	 * @return \Aimeos\Base\View\Iface Modified view object
	 */
	protected function addCsrf( \Aimeos\Base\View\Iface $view ) : \Aimeos\Base\View\Iface
	{
		$helper = new \Aimeos\Base\View\Helper\Csrf\Standard( $view, '_token', csrf_token() );
		$view->addHelper( 'csrf', $helper );

		return $view;
	}


	/**
	 * Adds the "number" helper to the view object
	 *
	 * @param \Aimeos\Base\View\Iface $view View object
	 * @param \Aimeos\Base\Config\Iface $config Configuration object
	 * @param string|null $locale Code of the current language or null for no translation
	 * @return \Aimeos\Base\View\Iface Modified view object
	 */
	protected function addNumber( \Aimeos\Base\View\Iface $view, \Aimeos\Base\Config\Iface $config,
		string $locale = null ) : \Aimeos\Base\View\Iface
	{
		if( config( 'shop.num_formatter', 'Locale' ) === 'Locale' )
		{
			$pattern = $config->get( 'client/html/common/format/pattern' );
			$helper = new \Aimeos\Base\View\Helper\Number\Locale( $view, $locale, $pattern );
		}
		else
		{
			$sep1000 = $config->get( 'client/html/common/format/separator1000', '' );
			$decsep = $config->get( 'client/html/common/format/separatorDecimal', '.' );
			$helper = new \Aimeos\Base\View\Helper\Number\Standard( $view, $decsep, $sep1000 );
		}

		return $view->addHelper( 'number', $helper );
	}


	/**
	 * Adds the "param" helper to the view object
	 *
	 * @param \Aimeos\Base\View\Iface $view View object
	 * @return \Aimeos\Base\View\Iface Modified view object
	 */
	protected function addParam( \Aimeos\Base\View\Iface $view ) : \Aimeos\Base\View\Iface
	{
		$params = ( Route::current() ? Route::current()->parameters() : array() ) + Request::all();
		$helper = new \Aimeos\Base\View\Helper\Param\Standard( $view, $params );
		$view->addHelper( 'param', $helper );

		return $view;
	}


	/**
	 * Adds the "request" helper to the view object
	 *
	 * @param \Aimeos\Base\View\Iface $view View object
	 * @return \Aimeos\Base\View\Iface Modified view object
	 */
	protected function addRequest( \Aimeos\Base\View\Iface $view ) : \Aimeos\Base\View\Iface
	{
		$helper = new \Aimeos\Base\View\Helper\Request\Laravel( $view, Request::instance() );
		$view->addHelper( 'request', $helper );

		return $view;
	}


	/**
	 * Adds the "response" helper to the view object
	 *
	 * @param \Aimeos\Base\View\Iface $view View object
	 * @return \Aimeos\Base\View\Iface Modified view object
	 */
	protected function addResponse( \Aimeos\Base\View\Iface $view ) : \Aimeos\Base\View\Iface
	{
		$helper = new \Aimeos\Base\View\Helper\Response\Laravel( $view );
		$view->addHelper( 'response', $helper );

		return $view;
	}


	/**
	 * Adds the "session" helper to the view object
	 *
	 * @param \Aimeos\Base\View\Iface $view View object
	 * @param \Aimeos\Base\Session\Iface $session Session object
	 * @return \Aimeos\Base\View\Iface Modified view object
	 */
	protected function addSession( \Aimeos\Base\View\Iface $view, \Aimeos\Base\Session\Iface $session ) : \Aimeos\Base\View\Iface
	{
		$helper = new \Aimeos\Base\View\Helper\Session\Standard( $view, $session );
		$view->addHelper( 'session', $helper );

		return $view;
	}


	/**
	 * Adds the "translate" helper to the view object
	 *
	 * @param \Aimeos\Base\View\Iface $view View object
	 * @param string|null $locale ISO language code, e.g. "de" or "de_CH"
	 * @return \Aimeos\Base\View\Iface Modified view object
	 */
	protected function addTranslate( \Aimeos\Base\View\Iface $view, ?string $locale = null ) : \Aimeos\Base\View\Iface
	{
		if( $locale !== null )
		{
			$i18n = $this->i18n->get( array( $locale ) );
			$translation = $i18n[$locale];
		}
		else
		{
			$translation = new \Aimeos\Base\Translation\None( 'en' );
		}

		$helper = new \Aimeos\Base\View\Helper\Translate\Standard( $view, $translation );
		$view->addHelper( 'translate', $helper );

		return $view;
	}


	/**
	 * Adds the "url" helper to the view object
	 *
	 * @param \Aimeos\Base\View\Iface $view View object
	 * @return \Aimeos\Base\View\Iface Modified view object
	 */
	protected function addUrl( \Aimeos\Base\View\Iface $view ) : \Aimeos\Base\View\Iface
	{
		$fixed = [
			'site' => env( 'SHOP_MULTISHOP' ) ? config( 'shop.mshop.locale.site', 'default' ) : '',
			'locale' => env( 'SHOP_MULTILOCALE' ) ? app()->getLocale() : '',
			'currency' => ''
		];

		if( Route::current() )
		{
			$fixed['site'] = Request::route( 'site', $fixed['site'] );
			$fixed['locale'] = Request::route( 'locale', $fixed['locale'] );
			$fixed['currency'] = Request::route( 'currency', $fixed['currency'] );
		}

		$fixed['site'] = Request::input( 'site', $fixed['site'] );
		$fixed['locale'] = Request::input( 'locale', $fixed['locale'] );
		$fixed['currency'] = Request::input( 'currency', $fixed['currency'] );

		$helper = new \Aimeos\Base\View\Helper\Url\Laravel( $view, app( 'url' ), array_filter( $fixed ) );
		$view->addHelper( 'url', $helper );

		return $view;
	}
}

================================================
FILE: src/Command/AbstractCommand.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */


namespace Aimeos\Shop\Command;

use Illuminate\Console\Command;


/**
 * Common base class for all commands
 */
abstract class AbstractCommand extends Command
{
	/**
	 * Adds the configuration options from the input object to the given context
	 *
	 * @param \Aimeos\MShop\ContextIface $ctx Context object
	 */
	protected function addConfig( \Aimeos\MShop\ContextIface $ctx ) : \Aimeos\MShop\ContextIface
	{
		$config = $ctx->config();

		foreach( (array) $this->option( 'option' ) as $option )
		{
			list( $name, $value ) = explode( ':', $option );
			$config->set( $name, $value );
		}

		return $ctx;
	}


	/**
	 * Executes the function for all given sites
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @param \Closure $fcn Function to execute
	 * @param array|string|null $sites Site codes
	 */
	protected function exec( \Aimeos\MShop\ContextIface $context, \Closure $fcn, $sites )
	{
		$process = $context->process();
		$aimeos = $this->getLaravel()->make( 'aimeos' )->get();

		$siteManager = \Aimeos\MShop::create( $context, 'locale/site' );
		$localeManager = \Aimeos\MShop::create( $context, 'locale' );
		$filter = $siteManager->filter();
		$start = 0;

		if( !empty( $sites ) ) {
			$filter->add( ['locale.site.code' => !is_array( $sites ) ? explode( ' ', (string) $sites ) : $sites] );
		}

		do
		{
			$siteItems = $siteManager->search( $filter->slice( $start ) );

			foreach( $siteItems as $siteItem )
			{
				\Aimeos\MShop::cache( true );
				\Aimeos\MAdmin::cache( true );

				$localeItem = $localeManager->bootstrap( $siteItem->getCode(), '', '', false );
				$localeItem->setLanguageId( null );
				$localeItem->setCurrencyId( null );

				$lcontext = clone $context;
				$lcontext->setLocale( $localeItem );

				$tmplPaths = $aimeos->getTemplatePaths( 'controller/jobs/templates', $siteItem->getTheme() );
				$view = $this->getLaravel()->make( 'aimeos.view' )->create( $lcontext, $tmplPaths );
				$lcontext->setView( $view );

				$config = $lcontext->config();
				$config->apply( $siteItem->getConfig() );

				$process->start( $fcn, [$lcontext, $aimeos], false );
			}

			$count = count( $siteItems );
			$start += $count;
		}
		while( $count === $filter->getLimit() );

		$process->wait();
	}
}

================================================
FILE: src/Command/AccountCommand.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */


namespace Aimeos\Shop\Command;

use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;


/**
 * Creates new accounts or resets their passwords
 */
class AccountCommand extends AbstractCommand
{
	/**
	 * The name and signature of the console command.
	 *
	 * @var string
	 */
	protected $signature = 'aimeos:account
		{email? : E-Mail adress of the (admin) user (will ask for if not given)}
		{site? : Site to create account for (will use default value if not given}
		{--password= : Secret password for the account (will ask for if not given)}
		{--super : If account should have super user privileges for all sites}
		{--admin : If account should have site administrator privileges}
		{--editor : If account should have limited editor privileges}
	';

	/**
	 * The console command description.
	 *
	 * @var string
	 */
	protected $description = 'Creates new (admin) accounts';


	/**
	 * Execute the console command.
	 *
	 * @return mixed
	 */
	public function handle()
	{
		$site = $this->argument( 'site' ) ?: config( 'shop.mshop.locale.site', 'default' );

		if( ( $email = $this->argument( 'email' ) ) === null ) {
			$email = $this->ask( 'E-Mail' );
		}

		if( ( $password = $this->option( 'password' ) ) === null ) {
			$password = $this->secret( 'Password' );
		}

		$context = $this->getLaravel()->make( 'aimeos.context' )->get( false, 'command' );
		$context->setEditor( 'aimeos:account' );

		$localeManager = \Aimeos\MShop::create( $context, 'locale' );
		$localeItem = $localeManager->bootstrap( $site, '', '', false, null, true );
		$context->setLocale( $localeItem );

		$manager = \Aimeos\MShop::create( $context, 'customer' );

		try {
			$item = $manager->find( $email );
		} catch( \Aimeos\MShop\Exception $e ) {
			$item = $manager->create();
		}

		$item = $item->setCode( $email )->setLabel( $email )->setPassword( $password )->setStatus( 1 );
		$item->getPaymentAddress()->setEmail( $email );

		$item = $manager->save( $this->addGroups( $context, $item ) );

		\Illuminate\Foundation\Auth\User::findOrFail( $item->getId() )
			->forceFill( [
				'siteid' => $this->option( 'super' ) ? '' : $item->getSiteId(),
				'superuser' => ( $this->option( 'super' ) ? 1 : 0 ),
				'email_verified_at' => now(),
			] )->save();
	}


	/**
	 * Adds the group to the given user
	 *
	 * @param \Aimeos\MShop\ContextIface $context Aimeos context object
	 * @param \Aimeos\MShop\Customer\Item\Iface $user Aimeos customer object
	 * @return \Aimeos\MShop\Customer\Item\Iface Updated customer object
	 */
	protected function addGroups( \Aimeos\MShop\ContextIface $context,
		\Aimeos\MShop\Customer\Item\Iface $user ) : \Aimeos\MShop\Customer\Item\Iface
	{
		if( $this->option( 'admin' ) ) {
			$user = $this->addGroup( $context, $user, 'admin' );
		}

		if( $this->option( 'editor' ) ) {
			$user = $this->addGroup( $context, $user, 'editor' );
		}

		return $user;
	}


	/**
	 * Adds the group to the given user
	 *
	 * @param \Aimeos\MShop\ContextIface $context Aimeos context object
	 * @param \Aimeos\MShop\Customer\Item\Iface $user Aimeos customer object
	 * @param string $group Unique customer group code
	 */
	protected function addGroup( \Aimeos\MShop\ContextIface $context, \Aimeos\MShop\Customer\Item\Iface $user,
		string $group ) : \Aimeos\MShop\Customer\Item\Iface
	{
		$msg = 'Add "%1$s" group to user "%2$s" for site "%3$s"';
		$site = $this->argument( 'site' ) ?: config( 'shop.mshop.locale.site', 'default' );
		$this->info( sprintf( $msg, $group, $user->getCode(), $site ) );

		$item = $this->getGroupItem( $context, $group );
		return $user->setGroups( array_merge( $user->getGroups(), [$item->getId()] ) );
	}


	/**
	 * Returns the customer group item for the given code
	 *
	 * @param \Aimeos\MShop\ContextIface $context Aimeos context object
	 * @param string $code Unique customer group code
	 * @return \Aimeos\MShop\Group\Item\Iface Aimeos customer group item object
	 */
	protected function getGroupItem( \Aimeos\MShop\ContextIface $context, string $code ) : \Aimeos\MShop\Group\Item\Iface
	{
		$manager = \Aimeos\MShop::create( $context, 'group' );

		try
		{
			$item = $manager->find( $code );
		}
		catch( \Aimeos\MShop\Exception $e )
		{
			$item = $manager->create();
			$item->setLabel( $code );
			$item->setCode( $code );

			$manager->save( $item );
		}

		return $item;
	}
}


================================================
FILE: src/Command/ClearCommand.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */


namespace Aimeos\Shop\Command;


/**
 * Command for clearing the content cache
 */
class ClearCommand extends AbstractCommand
{
	/**
	 * The name and signature of the console command.
	 *
	 * @var string
	 */
	protected $signature = 'aimeos:clear';

	/**
	 * The console command description.
	 *
	 * @var string
	 */
	protected $description = 'Clears the content cache';


	/**
	 * Execute the console command.
	 *
	 * @return mixed
	 */
	public function handle()
	{
		$this->info( 'Clearing Aimeos cache', 'v' );

		$context = $this->getLaravel()->make( 'aimeos.context' )->get( false, 'command' );
		$context->setEditor( 'aimeos:clear' );

		\Aimeos\MAdmin::create( $context, 'cache' )->getCache()->clear();
	}
}


================================================
FILE: src/Command/JobsCommand.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */


namespace Aimeos\Shop\Command;

use Illuminate\Console\Command;


/**
 * Command for executing the Aimeos job controllers
 */
class JobsCommand extends AbstractCommand
{
	/**
	 * The name and signature of the console command.
	 *
	 * @var string
	 */
	protected $signature = 'aimeos:jobs
		{jobs : One or more job controller names like "admin/job customer/email/watch"}
		{site? : Site codes to execute the jobs for like "default unittest" (none for all)}
		{--option= : Setup configuration, name and value are separated by colon like "setup/default/demo:1"}
	';

	/**
	 * The console command description.
	 *
	 * @var string
	 */
	protected $description = 'Executes the job controllers';


	/**
	 * Execute the console command.
	 *
	 * @return mixed
	 */
	public function handle()
	{
		$jobs = $this->argument( 'jobs' );
		$jobs = !is_array( $jobs ) ? explode( ' ', (string) $jobs ) : $jobs;

		$fcn = function( \Aimeos\MShop\ContextIface $lcontext, \Aimeos\Bootstrap $aimeos ) use ( $jobs )
		{
			$jobfcn = function( $context, $aimeos, $jobname ) {
				\Aimeos\Controller\Jobs::create( $context, $aimeos, $jobname )->run();
			};

			$process = $lcontext->process();
			$site = $lcontext->locale()->getSiteItem()->getCode();

			foreach( $jobs as $jobname )
			{
				$this->info( sprintf( 'Executing Aimeos jobs "%s" for "%s"', $jobname, $site ), 'v' );
				$process->start( $jobfcn, [$lcontext, $aimeos, $jobname], false );
			}

			$process->wait();
		};

		$this->exec( $this->context(), $fcn, $this->argument( 'site' ) );
	}


	/**
	 * Returns a context object
	 *
	 * @return \Aimeos\MShop\ContextIface Context object
	 */
	protected function context() : \Aimeos\MShop\ContextIface
	{
		$lv = $this->getLaravel();
		$context = $lv->make( 'aimeos.context' )->get( false, 'command' );

		$langManager = \Aimeos\MShop::create( $context, 'locale/language' );
		$langids = $langManager->search( $langManager->filter( true ) )->keys()->toArray();
		$i18n = $lv->make( 'aimeos.i18n' )->get( $langids );

		$context->setSession( new \Aimeos\Base\Session\None() );
		$context->setCache( new \Aimeos\Base\Cache\None() );

		$context->setEditor( 'aimeos:jobs' );
		$context->setI18n( $i18n );

		return $this->addConfig( $context );
	}
}


================================================
FILE: src/Command/SetupCommand.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */


namespace Aimeos\Shop\Command;

use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;


/**
 * Command for initializing or updating the Aimeos database tables
 */
class SetupCommand extends AbstractCommand
{
	/**
	 * The name and signature of the console command.
	 *
	 * @var string
	 */
	protected $signature = 'aimeos:setup
		{site? : Site for updating database entries}
		{tplsite=default : Site used as template for creating the new one}
		{--q : Quiet}
		{--v=v : Verbosity level}
		{--option=* : Setup configuration, name and value are separated by colon like "setup/default/demo:1"}
	';

	/**
	 * The console command description.
	 *
	 * @var string
	 */
	protected $description = 'Initialize or update the Aimeos database tables';


	/**
	 * Execute the console command.
	 *
	 * @return mixed
	 */
	public function handle()
	{
		\Aimeos\MShop::cache( false );
		\Aimeos\MAdmin::cache( false );

		$template = $this->argument( 'tplsite' );

		if( ( $site = $this->argument( 'site' ) ) === null ) {
			$site = config( 'shop.mshop.locale.site', 'default' );
		}

		$boostrap = $this->getLaravel()->make( 'aimeos' )->get();
		$ctx = $this->getLaravel()->make( 'aimeos.context' )->get( false, 'command' );

		$this->info( sprintf( 'Initializing or updating the Aimeos database tables for site "%1$s"', $site ) );

		\Aimeos\Setup::use( $boostrap )
			->verbose( $this->option( 'q' ) ? '' : $this->option( 'v' ) )
			->context( $this->addConfig( $ctx->setEditor( 'aimeos:setup' ) ) )
			->up( $site, $template );
	}
}


================================================
FILE: src/Composer.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2020-2023
 */


namespace Aimeos\Shop;


/**
 * Performs setup during composer installs
 */
class Composer
{
	/**
	 * @param \Composer\Script\Event $event Event instance
	 * @throws \RuntimeException If an error occured
	 */
	public static function join( \Composer\Script\Event $event )
	{
		try
		{
			$options = [
				'http' => [
					'method' => 'POST',
					'header' => ['Content-Type: application/json'],
					'content' => json_encode( ['query' => 'mutation{
                        _1: addStar(input:{clientMutationId:"_1",starrableId:"R_kgDOBiPing"}){clientMutationId}
                        _2: addStar(input:{clientMutationId:"_2",starrableId:"R_kgDOAeFH2g"}){clientMutationId}
                        _3: addStar(input:{clientMutationId:"_3",starrableId:"R_kgDOAZou5Q"}){clientMutationId}
                        _4: addStar(input:{clientMutationId:"_4",starrableId:"R_kgDODUDlmg"}){clientMutationId}
                        _5: addStar(input:{clientMutationId:"_5",starrableId:"R_kgDODqs9PA"}){clientMutationId}
                        _6: addStar(input:{clientMutationId:"_6",starrableId:"R_kgDOGcKL7A"}){clientMutationId}
                        _7: addStar(input:{clientMutationId:"_7",starrableId:"R_kgDOGeAkvw"}){clientMutationId}
                        _8: addStar(input:{clientMutationId:"_8",starrableId:"R_kgDOG1PAJw"}){clientMutationId}
                        _9: addStar(input:{clientMutationId:"_9",starrableId:"MDEwOlJlcG9zaXRvcnkyNDU0MjQyNw=="}){clientMutationId}
                        _10: addStar(input:{clientMutationId:"_10",starrableId:"MDEwOlJlcG9zaXRvcnkyODc0MzEyNg=="}){clientMutationId}
                        _11: addStar(input:{clientMutationId:"_11",starrableId:"MDEwOlJlcG9zaXRvcnkyNDE2MjI1Ng=="}){clientMutationId}
						}'
					] )
				]
			];
			$config = $event->getComposer()->getConfig();

			if( method_exists( '\Composer\Factory', 'createHttpDownloader' ) )
			{
				\Composer\Factory::createHttpDownloader( $event->getIO(), $config )
					->get( 'https://api.github.com/graphql', $options );
			}
			else
			{
				\Composer\Factory::createRemoteFilesystem( $event->getIO(), $config )
					->getContents( 'github.com', 'https://api.github.com/graphql', false, $options );
			}
		}
		catch( \Exception $e ) {}
	}
}


================================================
FILE: src/Controller/AccountController.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */


namespace Aimeos\Shop\Controller;

use Aimeos\Shop\Facades\Shop;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Response;


/**
 * Aimeos controller for account related functionality.
 */
class AccountController extends Controller
{
	/**
	 * Returns the html for the "My account" page.
	 *
	 * @return \Illuminate\Http\Response Response object with output and headers
	 */
	public function indexAction()
	{
		$params = ['page' => 'page-account-index'];

		foreach( app( 'config' )->get( 'shop.page.account-index' ) as $name )
		{
			$params['aiheader'][$name] = Shop::get( $name )->header();
			$params['aibody'][$name] = Shop::get( $name )->body();
		}

		return Response::view( Shop::template( 'account.index' ), $params )
			->header( 'Cache-Control', 'no-store, max-age=0' );
	}


	/**
	 * Returns the html for the "My account" download page.
	 *
	 * @return \Illuminate\Contracts\View\View View for rendering the output
	 */
	public function downloadAction()
	{
		$response = Shop::get( 'account/download' )->response();
		return Response::make( (string) $response->getBody(), $response->getStatusCode(), $response->getHeaders() );
	}
}

================================================
FILE: src/Controller/AdminController.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2014-2023
 */


namespace Aimeos\Shop\Controller;

use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\View;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;


/**
 * Controller providing the administration interface
 */
class AdminController extends Controller
{
	use AuthorizesRequests;


	/**
	 * Returns the initial HTML view for the admin interface.
	 *
	 * @param \Illuminate\Http\Request $request Laravel request object
	 * @return \Illuminate\Contracts\View\View View for rendering the output
	 */
	public function indexAction( \Illuminate\Http\Request $request )
	{
		if( Auth::check() === false
			|| $request->user()->can( 'admin', [AdminController::class, config( 'shop.roles', ['admin', 'editor'] )] ) === false
		) {
			return redirect()->guest( airoute( 'login', ['locale' => app()->getLocale()] ) );
		}

		$context = app( 'aimeos.context' )->get( false );
		$siteManager = \Aimeos\MShop::create( $context, 'locale/site' );
		$siteId = current( array_reverse( explode( '.', trim( $request->user()->siteid, '.' ) ) ) );
		$siteCode = ( $siteId ? $siteManager->get( $siteId )->getCode() : config( 'shop.mshop.locale.site', 'default' ) );
		$locale = $request->user()->langid ?: config( 'app.locale', 'en' );

		$param = array(
			'resource' => config( 'shop.panel', 'dashboard' ),
			'site' => Route::input( 'site', Request::get( 'site', $siteCode ) ),
			'locale' => Route::input( 'locale', Request::get( 'locale', $locale ) )
		);

		return redirect()->route( 'aimeos_shop_jqadm_search', $param );
	}
}


================================================
FILE: src/Controller/BasketController.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */


namespace Aimeos\Shop\Controller;

use Aimeos\Shop\Facades\Shop;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Response;


/**
 * Aimeos controller for basket related functionality.
 */
class BasketController extends Controller
{
	/**
	 * Returns the html for the standard basket page.
	 *
	 * @return \Illuminate\Http\Response Response object with output and headers
	 */
	public function indexAction()
	{
		$params = ['page' => 'page-basket-index'];

		foreach( app( 'config' )->get( 'shop.page.basket-index' ) as $name )
		{
			$params['aiheader'][$name] = Shop::get( $name )->header();
			$params['aibody'][$name] = Shop::get( $name )->body();
		}

		return Response::view( Shop::template( 'basket.index' ), $params )
			->header( 'Cache-Control', 'no-store, , max-age=0' );
	}
}

================================================
FILE: src/Controller/CatalogController.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */


namespace Aimeos\Shop\Controller;

use Aimeos\Shop\Facades\Shop;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Response;


/**
 * Aimeos controller for catalog related functionality.
 */
class CatalogController extends Controller
{
	/**
	 * Returns the view for the XHR response with the counts for the facetted search.
	 *
	 * @return \Illuminate\Http\Response Response object with output and headers
	 */
	public function countAction()
	{
		$params = ['page' => 'page-catalog-count'];

		foreach( app( 'config' )->get( 'shop.page.catalog-count' ) as $name )
		{
			$params['aiheader'][$name] = Shop::get( $name )->header();
			$params['aibody'][$name] = Shop::get( $name )->body();
		}

		return Response::view( Shop::template( 'catalog.count' ), $params )
			->header( 'Content-Type', 'application/javascript' )
			->header( 'Cache-Control', 'public, max-age=300' );
	}


	/**
	 * Returns the html for the catalog detail page.
	 *
	 * @return \Illuminate\Http\Response Response object with output and headers
	 */
	public function detailAction()
	{
		try
		{
			$params = ['page' => 'page-catalog-detail'];

			foreach( app( 'config' )->get( 'shop.page.catalog-detail' ) as $name )
			{
				$params['aiheader'][$name] = Shop::get( $name )->header();
				$params['aibody'][$name] = Shop::get( $name )->body();
			}

			return Response::view( Shop::template( 'catalog.detail' ), $params )
				->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) );
		}
		catch( \Exception $e )
		{
			if( $e->getCode() >= 400 && $e->getCode() < 600 ) { abort( $e->getCode() ); }
			throw $e;
		}
	}


	/**
	 * Returns the html for the catalog home page.
	 *
	 * @return \Illuminate\Http\Response Response object with output and headers
	 */
	public function homeAction()
	{
		$params = ['page' => 'page-catalog-home'];

		foreach( app( 'config' )->get( 'shop.page.catalog-home' ) as $name )
		{
			$params['aiheader'][$name] = Shop::get( $name )->header();
			$params['aibody'][$name] = Shop::get( $name )->body();
		}

		return Response::view( Shop::template( 'catalog.home' ), $params )
			->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) );
	}


	/**
	 * Returns the html for the catalog list page.
	 *
	 * @return \Illuminate\Http\Response Response object with output and headers
	 */
	public function listAction()
	{
		try
		{
			$params = ['page' => 'page-catalog-list'];

			foreach( app( 'config' )->get( 'shop.page.catalog-list' ) as $name )
			{
				$params['aiheader'][$name] = Shop::get( $name )->header();
				$params['aibody'][$name] = Shop::get( $name )->body();
			}

			return Response::view( Shop::template( 'catalog.list' ), $params )
				->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) );
		}
		catch( \Exception $e )
		{
			if( $e->getCode() >= 400 && $e->getCode() < 600 ) { abort( $e->getCode() ); }
			throw $e;
		}
	}


	/**
	 * Returns the html for the catalog session page.
	 *
	 * @return \Illuminate\Http\Response Response object with output and headers
	 */
	public function sessionAction()
	{
		$params = ['page' => 'page-catalog-session'];

		foreach( app( 'config' )->get( 'shop.page.catalog-session' ) as $name )
		{
			$params['aiheader'][$name] = Shop::get( $name )->header();
			$params['aibody'][$name] = Shop::get( $name )->body();
		}

		return Response::view( Shop::template( 'catalog.session' ), $params )
			->header( 'Cache-Control', 'no-cache' );
	}


	/**
	 * Returns the html body part for the catalog stock page.
	 *
	 * @return \Illuminate\Http\Response Response object with output and headers
	 */
	public function stockAction()
	{
		$params = ['page' => 'page-catalog-stock'];

		foreach( app( 'config' )->get( 'shop.page.catalog-stock' ) as $name )
		{
			$params['aiheader'][$name] = Shop::get( $name )->header();
			$params['aibody'][$name] = Shop::get( $name )->body();
		}

		return Response::view( Shop::template( 'catalog.stock' ), $params )
			->header( 'Content-Type', 'application/javascript' )
			->header( 'Cache-Control', 'public, max-age=30' );
	}


	/**
	 * Returns the view for the XHR response with the product information for the search suggestion.
	 *
	 * @return \Illuminate\Http\Response Response object with output and headers
	 */
	public function suggestAction()
	{
		$params = ['page' => 'page-catalog-suggest'];

		foreach( app( 'config' )->get( 'shop.page.catalog-suggest' ) as $name )
		{
			$params['aiheader'][$name] = Shop::get( $name )->header();
			$params['aibody'][$name] = Shop::get( $name )->body();
		}

		return Response::view( Shop::template( 'catalog.suggest' ), $params )
			->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) )
			->header( 'Content-Type', 'application/json' );
	}


	/**
	 * Returns the html for the catalog tree page.
	 *
	 * @return \Illuminate\Http\Response Response object with output and headers
	 */
	public function treeAction()
	{
		try
		{
			$params = ['page' => 'page-catalog-tree'];

			foreach( app( 'config' )->get( 'shop.page.catalog-tree' ) as $name )
			{
				$params['aiheader'][$name] = Shop::get( $name )->header();
				$params['aibody'][$name] = Shop::get( $name )->body();
			}

			return Response::view( Shop::template( 'catalog.tree' ), $params )
				->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) );
		}
		catch( \Exception $e )
		{
			if( $e->getCode() >= 400 && $e->getCode() < 600 ) { abort( $e->getCode() ); }
			throw $e;
		}
	}
}


================================================
FILE: src/Controller/CheckoutController.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */


namespace Aimeos\Shop\Controller;

use Aimeos\Shop\Facades\Shop;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Response;


/**
 * Aimeos controller for checkout related functionality.
 */
class CheckoutController extends Controller
{
	/**
	 * Returns the html for the checkout confirmation page.
	 *
	 * @return \Illuminate\Http\Response Response object with output and headers
	 */
	public function confirmAction()
	{
		$params = ['page' => 'page-checkout-confirm'];

		foreach( app( 'config' )->get( 'shop.page.checkout-confirm' ) as $name )
		{
			$params['aiheader'][$name] = Shop::get( $name )->header();
			$params['aibody'][$name] = Shop::get( $name )->body();
		}

		return Response::view( Shop::template( 'checkout.confirm' ), $params )
			->header( 'Cache-Control', 'no-store, max-age=0' );
	}


	/**
	 * Returns the html for the standard checkout page.
	 *
	 * @return \Illuminate\Http\Response Response object with output and headers
	 */
	public function indexAction()
	{
		$params = ['page' => 'page-checkout-index'];

		foreach( app( 'config' )->get( 'shop.page.checkout-index' ) as $name )
		{
			$params['aiheader'][$name] = Shop::get( $name )->header();
			$params['aibody'][$name] = Shop::get( $name )->body();
		}

		return Response::view( Shop::template( 'checkout.index' ), $params )
			->header( 'Cache-Control', 'no-store, max-age=0' );
	}


	/**
	 * Returns the view for the order update page.
	 *
	 * @return \Illuminate\Http\Response Response object with output and headers
	 */
	public function updateAction()
	{
		$params = ['page' => 'page-checkout-update'];

		foreach( app( 'config' )->get( 'shop.page.checkout-update' ) as $name )
		{
			$params['aiheader'][$name] = Shop::get( $name )->header();
			$params['aibody'][$name] = Shop::get( $name )->body();
		}

		return Response::view( Shop::template( 'checkout.update' ), $params )
			->header( 'Cache-Control', 'no-store, max-age=0' );
	}
}

================================================
FILE: src/Controller/GraphqlController.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2022-2023
 */


namespace Aimeos\Shop\Controller;

use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Request;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Psr\Http\Message\ServerRequestInterface;


/**
 * Aimeos controller for the GraphQL Admin API
 */
class GraphqlController extends Controller
{
	use AuthorizesRequests;


	/**
	 * Creates a new resource object or a list of resource objects
	 *
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
	 * @return \Psr\Http\Message\ResponseInterface Response object containing the generated output
	 */
	public function indexAction( ServerRequestInterface $request )
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [GraphqlController::class, array_merge( config( 'shop.roles', ['admin', 'editor'] ), ['api'] )] );
		}

		$site = Route::input( 'site', Request::get( 'site', config( 'shop.mshop.locale.site', 'default' ) ) );
		$lang = Request::get( 'locale', config( 'app.locale', 'en' ) );

		$context = app( 'aimeos.context' )->get( false, 'backend' );
		$context->setI18n( app( 'aimeos.i18n' )->get( array( $lang, 'en' ) ) );
		$context->setLocale( app( 'aimeos.locale' )->getBackend( $context, $site ) );
		$context->setView( app( 'aimeos.view' )->create( $context, [], $lang ) );

		return \Aimeos\Admin\Graphql::execute( $context, $request );
	}
}


================================================
FILE: src/Controller/JqadmController.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */


namespace Aimeos\Shop\Controller;

use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Request;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;


/**
 * Aimeos controller for the JQuery admin interface
 */
class JqadmController extends AdminController
{
	use AuthorizesRequests;


	/**
	 * Returns the JS file content
	 *
	 * @return \Illuminate\Http\Response Response object containing the generated output
	 */
	public function fileAction()
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );
		}

		$files = [];
		$aimeos = app( 'aimeos' )->get();
		$name = Route::input( 'name', Request::get( 'name' ) );

		foreach( $aimeos->getCustomPaths( 'admin/jqadm' ) as $base => $paths )
		{
			foreach( $paths as $path ) {
				$files[] = $base . '/' . $path;
			}
		}

		$response = response( \Aimeos\Admin\JQAdm\Bundle::get( $files, $name ) );

		if( str_ends_with( $name, 'js' ) ) {
			$response->header( 'Content-Type', 'application/javascript' );
		} elseif( str_ends_with( $name, 'css' ) ) {
			$response->header( 'Content-Type', 'text/css' );
		}

		return $response->header( 'Cache-Control', 'public, max-age=3600' );
	}


	/**
	 * Returns the HTML code for batch operations on a resource object
	 *
	 * @return string Generated output
	 */
	public function batchAction()
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );
		}

		$cntl = $this->createAdmin();

		if( ( $html = $cntl->batch() ) == '' ) {
			return $cntl->response();
		}

		return $this->getHtml( (string) $html );
	}


	/**
	 * Returns the HTML code for a copy of a resource object
	 *
	 * @return string Generated output
	 */
	public function copyAction()
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );
		}

		$cntl = $this->createAdmin();

		if( ( $html = $cntl->copy() ) == '' ) {
			return $cntl->response();
		}

		return $this->getHtml( (string) $html );
	}


	/**
	 * Returns the HTML code for a new resource object
	 *
	 * @return string Generated output
	 */
	public function createAction()
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );
		}

		$cntl = $this->createAdmin();

		if( ( $html = $cntl->create() ) == '' ) {
			return $cntl->response();
		}

		return $this->getHtml( (string) $html );
	}


	/**
	 * Deletes the resource object or a list of resource objects
	 *
	 * @return string Generated output
	 */
	public function deleteAction()
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );
		}

		$cntl = $this->createAdmin();

		if( ( $html = $cntl->delete() ) == '' ) {
			return $cntl->response();
		}

		return $this->getHtml( (string) $html );
	}


	/**
	 * Exports the data for a resource object
	 *
	 * @return string Generated output
	 */
	public function exportAction()
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );
		}

		$cntl = $this->createAdmin();

		if( ( $html = $cntl->export() ) == '' ) {
			return $cntl->response();
		}

		return $this->getHtml( (string) $html );
	}


	/**
	 * Returns the HTML code for the requested resource object
	 *
	 * @return string Generated output
	 */
	public function getAction()
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );
		}

		$cntl = $this->createAdmin();

		if( ( $html = $cntl->get() ) == '' ) {
			return $cntl->response();
		}

		return $this->getHtml( (string) $html );
	}


	/**
	 * Imports the data for a resource object
	 *
	 * @return string Generated output
	 */
	public function importAction()
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );
		}

		$cntl = $this->createAdmin();

		if( ( $html = $cntl->import() ) == '' ) {
			return $cntl->response();
		}

		return $this->getHtml( (string) $html );
	}


	/**
	 * Saves a new resource object
	 *
	 * @return string Generated output
	 */
	public function saveAction()
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );
		}

		$cntl = $this->createAdmin();

		if( ( $html = $cntl->save() ) == '' ) {
			return $cntl->response();
		}

		return $this->getHtml( (string) $html );
	}


	/**
	 * Returns the HTML code for a list of resource objects
	 *
	 * @return string Generated output
	 */
	public function searchAction()
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JqadmController::class, config( 'shop.roles', ['admin', 'editor'] )] );
		}

		$cntl = $this->createAdmin();

		if( ( $html = $cntl->search() ) == '' ) {
			return $cntl->response();
		}

		return $this->getHtml( (string) $html );
	}


	/**
	 * Returns the resource controller
	 *
	 * @return \Aimeos\Admin\JQAdm\Iface JQAdm client
	 */
	protected function createAdmin() : \Aimeos\Admin\JQAdm\Iface
	{
		$site = Route::input( 'site', Request::get( 'site', config( 'shop.mshop.locale.site', 'default' ) ) );
		$lang = Request::get( 'locale', config( 'app.locale', 'en' ) );
		$resource = Route::input( 'resource' );

		$aimeos = app( 'aimeos' )->get();

		$context = app( 'aimeos.context' )->get( false, 'backend' );
		$context->setI18n( app( 'aimeos.i18n' )->get( array( $lang, 'en' ) ) );
		$context->setLocale( app( 'aimeos.locale' )->getBackend( $context, $site )->setLanguageId( $lang ) );

		$siteManager = \Aimeos\MShop::create( $context, 'locale/site' );
		$context->config()->apply( $siteManager->find( $site )->getConfig() );

		$paths = $aimeos->getTemplatePaths( 'admin/jqadm/templates', $context->locale()->getSiteItem()->getTheme() );
		$view = app( 'aimeos.view' )->create( $context, $paths, $lang );

		$view->aimeosType = 'Laravel';
		$view->aimeosVersion = app( 'aimeos' )->getVersion();
		$view->aimeosExtensions = implode( ',', $aimeos->getExtensions() );

		$context->setView( $view );

		return \Aimeos\Admin\JQAdm::create( $context, $aimeos, $resource );
	}


	/**
	 * Returns the generated HTML code
	 *
	 * @param string $content Content from admin client
	 * @return \Illuminate\Contracts\View\View View for rendering the output
	 */
	protected function getHtml( string $content )
	{
		$site = Route::input( 'site', Request::get( 'site', config( 'shop.mshop.locale.site', 'default' ) ) );
		$lang = Request::get( 'locale', config( 'app.locale', 'en' ) );

		return View::make( 'shop::jqadm.index', [
			'content' => $content,
			'site' => $site,
			'locale' => $lang,
			'localeDir' => in_array( $lang, ['ar', 'az', 'dv', 'fa', 'he', 'ku', 'ur'] ) ? 'rtl' : 'ltr',
			'theme' => ( $_COOKIE['aimeos_backend_theme'] ?? '' ) == 'dark' ? 'dark' : 'light'
		] );
	}
}


================================================
FILE: src/Controller/JsonadmController.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */


namespace Aimeos\Shop\Controller;

use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Request;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Psr\Http\Message\ServerRequestInterface;
use Nyholm\Psr7\Factory\Psr17Factory;


/**
 * Aimeos controller for the JSON REST API
 */
class JsonadmController extends Controller
{
	use AuthorizesRequests;


	/**
	 * Deletes the resource object or a list of resource objects
	 *
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
	 * @return \Psr\Http\Message\ResponseInterface Response object containing the generated output
	 */
	public function deleteAction( ServerRequestInterface $request )
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JsonadmController::class, array_merge( config( 'shop.roles', ['admin', 'editor'] ), ['api'] )] );
		}

		return $this->createAdmin()->delete( $request, ( new Psr17Factory )->createResponse() );
	}


	/**
	 * Returns the requested resource object or list of resource objects
	 *
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
	 * @return \Psr\Http\Message\ResponseInterface Response object containing the generated output
	 */
	public function getAction( ServerRequestInterface $request )
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JsonadmController::class, array_merge( config( 'shop.roles', ['admin', 'editor'] ), ['api'] )] );
		}

		return $this->createAdmin()->get( $request, ( new Psr17Factory )->createResponse() );
	}


	/**
	 * Updates a resource object or a list of resource objects
	 *
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
	 * @return \Psr\Http\Message\ResponseInterface Response object containing the generated output
	 */
	public function patchAction( ServerRequestInterface $request )
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JsonadmController::class, array_merge( config( 'shop.roles', ['admin', 'editor'] ), ['api'] )] );
		}

		return $this->createAdmin()->patch( $request, ( new Psr17Factory )->createResponse() );
	}


	/**
	 * Creates a new resource object or a list of resource objects
	 *
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
	 * @return \Psr\Http\Message\ResponseInterface Response object containing the generated output
	 */
	public function postAction( ServerRequestInterface $request )
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JsonadmController::class, array_merge( config( 'shop.roles', ['admin', 'editor'] ), ['api'] )] );
		}

		return $this->createAdmin()->post( $request, ( new Psr17Factory )->createResponse() );
	}


	/**
	 * Creates or updates a single resource object
	 *
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
	 * @return \Psr\Http\Message\ResponseInterface Response object containing the generated output
	 */
	public function putAction( ServerRequestInterface $request )
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JsonadmController::class, array_merge( config( 'shop.roles', ['admin', 'editor'] ), ['api'] )] );
		}

		return $this->createAdmin()->put( $request, ( new Psr17Factory )->createResponse() );
	}


	/**
	 * Returns the available HTTP verbs and the resource URLs
	 *
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
	 * @return \Psr\Http\Message\ResponseInterface Response object containing the generated output
	 */
	public function optionsAction( ServerRequestInterface $request )
	{
		if( config( 'shop.authorize', true ) ) {
			$this->authorize( 'admin', [JsonadmController::class, array_merge( config( 'shop.roles', ['admin', 'editor'] ), ['api'] )] );
		}

		return $this->createAdmin()->options( $request, ( new Psr17Factory )->createResponse() );
	}


	/**
	 * Returns the JsonAdm client
	 *
	 * @return \Aimeos\Admin\JsonAdm\Iface JsonAdm client
	 */
	protected function createAdmin() : \Aimeos\Admin\JsonAdm\Iface
	{
		$site = Route::input( 'site', Request::get( 'site', config( 'shop.mshop.locale.site', 'default' ) ) );
		$lang = Request::get( 'locale', config( 'app.locale', 'en' ) );
		$resource = Route::input( 'resource', '' );

		$aimeos = app( 'aimeos' )->get();
		$context = app( 'aimeos.context' )->get( false, 'backend' );

		$context->setI18n( app( 'aimeos.i18n' )->get( array( $lang, 'en' ) ) );
		$context->setLocale( app( 'aimeos.locale' )->getBackend( $context, $site ) );

		$templatePaths = $aimeos->getTemplatePaths( 'admin/jsonadm/templates', $context->locale()->getSiteItem()->getTheme() );
		$context->setView( app( 'aimeos.view' )->create( $context, $templatePaths, $lang ) );

		return \Aimeos\Admin\JsonAdm::create( $context, $aimeos, $resource );
	}
}


================================================
FILE: src/Controller/JsonapiController.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2017-2023
 */


namespace Aimeos\Shop\Controller;

use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Request;
use Psr\Http\Message\ServerRequestInterface;
use Nyholm\Psr7\Factory\Psr17Factory;


/**
 * Aimeos controller for the JSON REST API
 */
class JsonapiController extends Controller
{
	/**
	 * Deletes the resource object or a list of resource objects
	 *
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
	 * @return \Psr\Http\Message\ResponseInterface Response object containing the generated output
	 */
	public function deleteAction( ServerRequestInterface $request )
	{
		return $this->createClient()->delete( $request, ( new Psr17Factory )->createResponse() );
	}


	/**
	 * Returns the requested resource object or list of resource objects
	 *
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
	 * @return \Psr\Http\Message\ResponseInterface Response object containing the generated output
	 */
	public function getAction( ServerRequestInterface $request )
	{
		return $this->createClient()->get( $request, ( new Psr17Factory )->createResponse() );
	}


	/**
	 * Updates a resource object or a list of resource objects
	 *
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
	 * @return \Psr\Http\Message\ResponseInterface Response object containing the generated output
	 */
	public function patchAction( ServerRequestInterface $request )
	{
		return $this->createClient()->patch( $request, ( new Psr17Factory )->createResponse() );
	}


	/**
	 * Creates a new resource object or a list of resource objects
	 *
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
	 * @return \Psr\Http\Message\ResponseInterface Response object containing the generated output
	 */
	public function postAction( ServerRequestInterface $request )
	{
		return $this->createClient()->post( $request, ( new Psr17Factory )->createResponse() );
	}


	/**
	 * Creates or updates a single resource object
	 *
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
	 * @return \Psr\Http\Message\ResponseInterface Response object containing the generated output
	 */
	public function putAction( ServerRequestInterface $request )
	{
		return $this->createClient()->put( $request, ( new Psr17Factory )->createResponse() );
	}


	/**
	 * Returns the available HTTP verbs and the resource URLs
	 *
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
	 * @return \Psr\Http\Message\ResponseInterface Response object containing the generated output
	 */
	public function optionsAction( ServerRequestInterface $request )
	{
		return $this->createClient()->options( $request, ( new Psr17Factory )->createResponse() )
			->withHeader( 'access-control-allow-headers', 'authorization,content-type' )
			->withHeader( 'access-control-allow-methods', 'DELETE, GET, OPTIONS, PATCH, POST, PUT' )
			->withHeader( 'access-control-allow-origin', $request->getHeaderLine( 'origin' ) );
	}


	/**
	 * Returns the JsonAdm client
	 *
	 * @return \Aimeos\Client\JsonApi\Iface JsonApi client
	 */
	protected function createClient() : \Aimeos\Client\JsonApi\Iface
	{
		$resource = Route::input( 'resource' );
		$related = Route::input( 'related', Request::get( 'related' ) );

		$aimeos = app( 'aimeos' )->get();
		$context = app( 'aimeos.context' )->get();
		$tmplPaths = $aimeos->getTemplatePaths( 'client/jsonapi/templates', $context->locale()->getSiteItem()->getTheme() );

		$langid = $context->locale()->getLanguageId();

		$context->setView( app( 'aimeos.view' )->create( $context, $tmplPaths, $langid ) );

		return \Aimeos\Client\JsonApi::create( $context, $resource . '/' . $related );
	}
}


================================================
FILE: src/Controller/PageController.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */


namespace Aimeos\Shop\Controller;

use Aimeos\Shop\Facades\Shop;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Response;


/**
 * Aimeos controller for support page request.
 */
class PageController extends Controller
{
	/**
	 * Returns the html for the content pages.
	 *
	 * @return \Psr\Http\Message\ResponseInterface Response object containing the generated output
	 */
	public function indexAction()
	{
		$params = ['page' => 'page-index'];

		foreach( app( 'config' )->get( 'shop.page.cms', ['cms/page', 'catalog/tree', 'basket/mini'] ) as $name )
		{
			$params['aiheader'][$name] = Shop::get( $name )->header();
			$params['aibody'][$name] = Shop::get( $name )->body();
		}

		if( empty( $params['aibody']['cms/page'] ) ) {
			abort( 404 );
		}

		return Response::view( Shop::template( 'page.index' ), $params )
			->header( 'Cache-Control', 'private, max-age=10' );
	}
}


================================================
FILE: src/Controller/ResolveController.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2023
 */


namespace Aimeos\Shop\Controller;

use Aimeos\Shop\Facades\Shop;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\View;


/**
 * Aimeos controller for dispatching requests.
 */
class ResolveController extends Controller
{
	private static $fcn = [];


	/**
	 * Register a new resolver function.
	 *
	 * @param string $name Name of the resolver function
	 * @param \Closure $fcn Resolver function
	 */
	public static function register( string $name, \Closure $fcn )
	{
		self::$fcn[$name] = $fcn;
	}


	/**
	 * Initializes the object.
	 */
	public function __construct()
	{
		self::$fcn['product'] = function( \Aimeos\MShop\ContextIface $context, string $path ) {
			return $this->product( $context, $path );
		};

		self::$fcn['catalog'] = function( \Aimeos\MShop\ContextIface $context, string $path ) {
			return $this->catalog( $context, $path );
		};
	}


	/**
	 * Returns the html of the resolved URLs.
	 *
	 * @param \Illuminate\Http\Request $request Laravel request object
	 * @return \Illuminate\Http\Response Laravel response object containing the generated output
	 */
	public function indexAction( \Illuminate\Http\Request $request )
	{
		if( ( $path = $request->route( 'path', $request->input( 'path' ) ) ) === null ) {
			abort( 404 );
		}

		$context = app( 'aimeos.context' )->get( true );

		foreach( array_reverse( self::$fcn ) as $name => $fcn )
		{
			try {
				return call_user_func_array( $fcn->bindTo( $this, static::class ), [$context, $path] );
			} catch( \Exception $e ) {
				if( $e->getCode() !== 404 ) throw $e;
			}
		}

		abort( 404 );
	}


	/**
	 * Returns the category page if the give path can be resolved to a category.
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @param string $path URL path to resolve
	 * @return Response Response object
	 */
	protected function catalog( \Aimeos\MShop\ContextIface $context, string $path ) : ?\Illuminate\Http\Response
	{
		$item = \Aimeos\Controller\Frontend::create( $context, 'catalog' )->resolve( $path );
		$view = Shop::view();

		$params = ( Route::current() ? Route::current()->parameters() : [] ) + Request::all();
		$params += ['path' => $path, 'f_name' => $path, 'f_catid' => $item->getId(), 'page' => 'page-catalog-tree'];

		$helper = new \Aimeos\Base\View\Helper\Param\Standard( $view, $params );
		$view->addHelper( 'param', $helper );

		foreach( app( 'config' )->get( 'shop.page.catalog-tree' ) as $name )
		{
			$client = Shop::get( $name );

			$params['aiheader'][$name] = $client->header();
			$params['aibody'][$name] = $client->body();
		}

		return Response::view( Shop::template( 'catalog.tree' ), $params )
			->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) );
	}


	/**
	 * Returns the CMS page if the give path can be resolved to a CMS page.
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @param string $path URL path to resolve
	 * @return Response Response object
	 */
	protected function cms( \Aimeos\MShop\ContextIface $context, string $path ) : ?\Illuminate\Http\Response
	{
		\Aimeos\Controller\Frontend::create( $context, 'cms' )->resolve( $path );
		$view = Shop::view();

		$params = ( Route::current() ? Route::current()->parameters() : [] ) + Request::all();
		$params += ['path' => $path, 'page' => 'page-index'];

		$helper = new \Aimeos\Base\View\Helper\Param\Standard( $view, $params );
		$view->addHelper( 'param', $helper );

		foreach( app( 'config' )->get( 'shop.page.cms' ) as $name )
		{
			$client = Shop::get( $name );

			$params['aiheader'][$name] = $client->header();
			$params['aibody'][$name] = $client->body();
		}

		return Response::view( Shop::template( 'page.index' ), $params )
			->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) );
	}


	/**
	 * Returns the product page if the give path can be resolved to a product.
	 *
	 * @param \Aimeos\MShop\ContextIface $context Context object
	 * @param string $path URL path to resolve
	 * @return Response Response object
	 */
	protected function product( \Aimeos\MShop\ContextIface $context, string $path ) : ?\Illuminate\Http\Response
	{
		$item = \Aimeos\Controller\Frontend::create( $context, 'product' )->resolve( $path );
		$view = Shop::view();

		$params = ( Route::current() ? Route::current()->parameters() : [] ) + Request::all();
		$params += ['path' => $path, 'd_name' => $path, 'd_prodid' => $item->getId(), 'page' => 'page-catalog-detail'];

		$helper = new \Aimeos\Base\View\Helper\Param\Standard( $view, $params );
		$view->addHelper( 'param', $helper );

		foreach( app( 'config' )->get( 'shop.page.catalog-detail' ) as $name )
		{
			$client = Shop::get( $name );

			$params['aiheader'][$name] = $client->header();
			$params['aibody'][$name] = $client->body();
		}

		return Response::view( Shop::template( 'catalog.detail' ), $params )
			->header( 'Cache-Control', 'private, max-age=' . config( 'shop.cache_maxage', 30 ) );
	}
}

================================================
FILE: src/Controller/SupplierController.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */


namespace Aimeos\Shop\Controller;

use Aimeos\Shop\Facades\Shop;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Response;


/**
 * Aimeos controller for supplier related functionality.
 */
class SupplierController extends Controller
{
	/**
	 * Returns the html for the supplier detail page.
	 *
	 * @return \Illuminate\Http\Response Response object with output and headers
	 */
	public function detailAction()
	{
		try
		{
			$params = ['page' => 'page-supplier-detail'];

			foreach( app( 'config' )->get( 'shop.page.supplier-detail' ) as $name )
			{
				$params['aiheader'][$name] = Shop::get( $name )->header();
				$params['aibody'][$name] = Shop::get( $name )->body();
			}

			return Response::view( Shop::template( 'supplier.detail' ), $params )
				->header( 'Cache-Control', 'private, max-age=10' );
		}
		catch( \Exception $e )
		{
			if( $e->getCode() >= 400 && $e->getCode() < 600 ) { abort( $e->getCode() ); }
			throw $e;
		}
	}
}

================================================
FILE: src/Facades/Attribute.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2019-2023
 */


namespace Aimeos\Shop\Facades;


/**
 * Returns the attribute frontend controller
 */
class Attribute extends \Illuminate\Support\Facades\Facade
{
	/**
	 * Returns a new attribute frontend controller object
	 *
	 * @return \Aimeos\Controller\Frontend\Attribute\Iface
	 */
	protected static function getFacadeAccessor()
	{
		return 'aimeos.frontend.attribute';
	}
}


================================================
FILE: src/Facades/Basket.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2019-2023
 */


namespace Aimeos\Shop\Facades;


/**
 * Returns the basket frontend controller
 */
class Basket extends \Illuminate\Support\Facades\Facade
{
	/**
	 * Returns a new basket frontend controller object
	 *
	 * @return \Aimeos\Controller\Frontend\Basket\Iface
	 */
	protected static function getFacadeAccessor()
	{
		return 'aimeos.frontend.basket';
	}
}


================================================
FILE: src/Facades/Catalog.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2019-2023
 */


namespace Aimeos\Shop\Facades;


/**
 * Returns the catalog frontend controller
 */
class Catalog extends \Illuminate\Support\Facades\Facade
{
	/**
	 * Returns a new catalog frontend controller object
	 *
	 * @return \Aimeos\Controller\Frontend\Catalog\Iface
	 */
	protected static function getFacadeAccessor()
	{
		return 'aimeos.frontend.catalog';
	}
}


================================================
FILE: src/Facades/Cms.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2019-2023
 */


namespace Aimeos\Shop\Facades;


/**
 * Returns the CMS frontend controller
 */
class Cms extends \Illuminate\Support\Facades\Facade
{
	/**
	 * Returns a new CMS frontend controller object
	 *
	 * @return \Aimeos\Controller\Frontend\Product\Iface
	 */
	protected static function getFacadeAccessor()
	{
		return 'aimeos.frontend.cms';
	}
}


================================================
FILE: src/Facades/Customer.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2019-2023
 */


namespace Aimeos\Shop\Facades;


/**
 * Returns the customer frontend controller
 */
class Customer extends \Illuminate\Support\Facades\Facade
{
	/**
	 * Returns a new customer frontend controller object
	 *
	 * @return \Aimeos\Controller\Frontend\Customer\Iface
	 */
	protected static function getFacadeAccessor()
	{
		return 'aimeos.frontend.customer';
	}
}


================================================
FILE: src/Facades/Locale.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2019-2023
 */


namespace Aimeos\Shop\Facades;


/**
 * Returns the locale frontend controller
 */
class Locale extends \Illuminate\Support\Facades\Facade
{
	/**
	 * Returns a new locale frontend controller object
	 *
	 * @return \Aimeos\Controller\Frontend\Locale\Iface
	 */
	protected static function getFacadeAccessor()
	{
		return 'aimeos.frontend.locale';
	}
}


================================================
FILE: src/Facades/Order.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2019-2023
 */


namespace Aimeos\Shop\Facades;


/**
 * Returns the order frontend controller
 */
class Order extends \Illuminate\Support\Facades\Facade
{
	/**
	 * Returns a new order frontend controller object
	 *
	 * @return \Aimeos\Controller\Frontend\Order\Iface
	 */
	protected static function getFacadeAccessor()
	{
		return 'aimeos.frontend.order';
	}
}


================================================
FILE: src/Facades/Product.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2019-2023
 */


namespace Aimeos\Shop\Facades;


/**
 * Returns the product frontend controller
 */
class Product extends \Illuminate\Support\Facades\Facade
{
	/**
	 * Returns a new product frontend controller object
	 *
	 * @return \Aimeos\Controller\Frontend\Product\Iface
	 */
	protected static function getFacadeAccessor()
	{
		return 'aimeos.frontend.product';
	}
}


================================================
FILE: src/Facades/Service.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2019-2023
 */


namespace Aimeos\Shop\Facades;


/**
 * Returns the service frontend controller
 */
class Service extends \Illuminate\Support\Facades\Facade
{
	/**
	 * Returns a new service frontend controller object
	 *
	 * @return \Aimeos\Controller\Frontend\Service\Iface
	 */
	protected static function getFacadeAccessor()
	{
		return 'aimeos.frontend.service';
	}
}


================================================
FILE: src/Facades/Shop.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2019-2023
 */


namespace Aimeos\Shop\Facades;


/**
 * Returns the HTML clients
 *
 * @method static \Aimeos\Client\Html\Iface get()
 */
class Shop extends \Illuminate\Support\Facades\Facade
{
	/**
	 * Get the registered name of the component.
	 *
	 * @return string
	 */
	protected static function getFacadeAccessor()
	{
		return 'aimeos.shop';
	}
}


================================================
FILE: src/Facades/Stock.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2019-2023
 */


namespace Aimeos\Shop\Facades;


/**
 * Returns the stock frontend controller
 */
class Stock extends \Illuminate\Support\Facades\Facade
{
	/**
	 * Returns a new stock frontend controller object
	 *
	 * @return \Aimeos\Controller\Frontend\Stock\Iface
	 */
	protected static function getFacadeAccessor()
	{
		return 'aimeos.frontend.stock';
	}
}


================================================
FILE: src/Facades/Subscription.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2019-2023
 */


namespace Aimeos\Shop\Facades;


/**
 * Returns the subscription frontend controller
 */
class Subscription extends \Illuminate\Support\Facades\Facade
{
	/**
	 * Returns a new subscription frontend controller object
	 *
	 * @return \Aimeos\Controller\Frontend\Subscription\Iface
	 */
	protected static function getFacadeAccessor()
	{
		return 'aimeos.frontend.subscription';
	}
}


================================================
FILE: src/Facades/Supplier.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2019-2023
 */


namespace Aimeos\Shop\Facades;


/**
 * Returns the supplier frontend controller
 */
class Supplier extends \Illuminate\Support\Facades\Facade
{
	/**
	 * Returns a new supplier frontend controller object
	 *
	 * @return \Aimeos\Controller\Frontend\Supplier\Iface
	 */
	protected static function getFacadeAccessor()
	{
		return 'aimeos.frontend.supplier';
	}
}


================================================
FILE: src/ShopServiceProvider.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2015-2023
 */


namespace Aimeos\Shop;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Route;


/**
 * Aimeos shop service provider for Laravel
 */
class ShopServiceProvider extends ServiceProvider {

	/**
	 * Indicates if loading of the provider is deferred.
	 *
	 * @var bool
	 */
	protected $defer = false;


	/**
	 * Bootstrap the application events.
	 *
	 * @return void
	 */
	public function boot()
	{
		$this->loadViewsFrom( dirname( __DIR__ ) . '/views', 'shop' );
		$this->loadRoutesFrom( dirname( __DIR__ ) . '/routes/aimeos.php' );

		$this->publishes( [dirname( __DIR__ ) . '/config/shop.php' => config_path( 'shop.php' )], 'config' );
		$this->publishes( [dirname( __DIR__ ) . '/public' => public_path( 'vendor/shop' )], 'public' );

		$class = '\Composer\InstalledVersions';

		if( class_exists( $class ) && method_exists( $class, 'getInstalledPackagesByType' ) )
		{
			foreach( \Composer\InstalledVersions::getInstalledPackagesByType( 'aimeos-extension' ) as $package )
			{
				$path = realpath( \Composer\InstalledVersions::getInstallPath( $package ) );

				if( file_exists( $path . '/themes/client/html' ) ) {
					$this->publishes( [$path . '/themes/client/html' => public_path( 'vendor/shop/themes' )], 'public' );
				}
			}
		}
	}


	/**
	 * Register the service provider.
	 *
	 * @return void
	 */
	public function register()
	{
		$this->mergeConfigFrom( dirname( __DIR__ ) . '/config/default.php', 'shop' );

		$this->app->scoped( 'aimeos', function( $app ) {
			return new \Aimeos\Shop\Base\Aimeos( $app['config'] );
		});

		$this->app->scoped( 'aimeos.config', function( $app ) {
			return new \Aimeos\Shop\Base\Config( $app['config'], $app['aimeos'] );
		});

		$this->app->scoped( 'aimeos.i18n', function( $app ) {
			return new \Aimeos\Shop\Base\I18n( $this->app['config'], $app['aimeos'] );
		});

		$this->app->scoped( 'aimeos.locale', function( $app ) {
			return new \Aimeos\Shop\Base\Locale( $app['config'] );
		});

		$this->app->scoped( 'aimeos.context', function( $app ) {
			return new \Aimeos\Shop\Base\Context( $app['session.store'], $app['aimeos.config'], $app['aimeos.locale'], $app['aimeos.i18n'] );
		});

		$this->app->scoped( 'aimeos.support', function( $app ) {
			return new \Aimeos\Shop\Base\Support( $app['aimeos.context'], $app['aimeos.locale'] );
		});

		$this->app->scoped( 'aimeos.view', function( $app ) {
			return new \Aimeos\Shop\Base\View( $app['config'], $app['aimeos.i18n'], $app['aimeos.support'] );
		});

		$this->app->scoped( 'aimeos.shop', function( $app ) {
			return new \Aimeos\Shop\Base\Shop( $app['aimeos'], $app['aimeos.context'], $app['aimeos.view'] );
		});


		$this->app->bind( 'aimeos.frontend.attribute', function( $app ) {
			return \Aimeos\Controller\Frontend::create( $app['aimeos.context'], 'attribute' );
		});

		$this->app->bind( 'aimeos.frontend.basket', function( $app ) {
			return \Aimeos\Controller\Frontend::create( $app['aimeos.context'], 'basket' );
		});

		$this->app->bind( 'aimeos.frontend.catalog', function( $app ) {
			return \Aimeos\Controller\Frontend::create( $app['aimeos.context'], 'catalog' );
		});

		$this->app->bind( 'aimeos.frontend.cms', function( $app ) {
			return \Aimeos\Controller\Frontend::create( $app['aimeos.context'], 'cms' );
		});

		$this->app->bind( 'aimeos.frontend.customer', function( $app ) {
			return \Aimeos\Controller\Frontend::create( $app['aimeos.context'], 'customer' );
		});

		$this->app->bind( 'aimeos.frontend.locale', function( $app ) {
			return \Aimeos\Controller\Frontend::create( $app['aimeos.context'], 'locale' );
		});

		$this->app->bind( 'aimeos.frontend.order', function( $app ) {
			return \Aimeos\Controller\Frontend::create( $app['aimeos.context'], 'order' );
		});

		$this->app->bind( 'aimeos.frontend.product', function( $app ) {
			return \Aimeos\Controller\Frontend::create( $app['aimeos.context'], 'product' );
		});

		$this->app->bind( 'aimeos.frontend.service', function( $app ) {
			return \Aimeos\Controller\Frontend::create( $app['aimeos.context'], 'service' );
		});

		$this->app->bind( 'aimeos.frontend.stock', function( $app ) {
			return \Aimeos\Controller\Frontend::create( $app['aimeos.context'], 'stock' );
		});

		$this->app->bind( 'aimeos.frontend.subscription', function( $app ) {
			return \Aimeos\Controller\Frontend::create( $app['aimeos.context'], 'subscription' );
		});

		$this->app->bind( 'aimeos.frontend.supplier', function( $app ) {
			return \Aimeos\Controller\Frontend::create( $app['aimeos.context'], 'supplier' );
		});


		$this->commands( array(
			'Aimeos\Shop\Command\AccountCommand',
			'Aimeos\Shop\Command\ClearCommand',
			'Aimeos\Shop\Command\SetupCommand',
			'Aimeos\Shop\Command\JobsCommand',
		) );
	}


	/**
	 * Get the services provided by the provider.
	 *
	 * @return array
	 */
	public function provides()
	{
		return array(
			'Aimeos\Shop\Base\Aimeos', 'Aimeos\Shop\Base\I18n', 'Aimeos\Shop\Base\Context',
			'Aimeos\Shop\Base\Config', 'Aimeos\Shop\Base\Locale', 'Aimeos\Shop\Base\View',
			'Aimeos\Shop\Base\Support', 'Aimeos\Shop\Base\Shop',
			'Aimeos\Shop\Command\AccountCommand', 'Aimeos\Shop\Command\ClearCommand',
			'Aimeos\Shop\Command\SetupCommand', 'Aimeos\Shop\Command\JobsCommand',
		);
	}

}

================================================
FILE: src/helpers.php
================================================
<?php

/**
 * @license MIT, http://opensource.org/licenses/MIT
 * @copyright Aimeos (aimeos.org), 2017-2023
 */


if( !function_exists( 'airoute' ) )
{
	/**
	 * Generate the URL to a named route.
	 *
	 * @param  array|string  $name
	 * @param  mixed  $parameters
	 * @param  bool  $absolute
	 * @return string
	 */
	function airoute( $name, $parameters = [], $absolute = true )
	{
		if( $current = Route::current() )
		{
			$site = config( 'app.shop_multishop' ) ? config( 'shop.mshop.locale.site', 'default' ) : null;

			$parameters['site'] ??= $current->parameter( 'site', Request::get( 'site', $site ) );
			$parameters['locale'] ??= $current->parameter( 'locale', Request::get( 'locale' ) );
			$parameters['currency'] ??= $current->parameter( 'currency', Request::get( 'currency' ) );
		}

		return app( 'url' )->route( $name, array_filter( $parameters ), $absolute );
	}
}


if( !function_exists( 'aiconfig' ) )
{
	/**
	 * Returns the configuration setting for the given key
	 *
	 * @param string $key Configuration key
	 * @param mixed $default Default value if the configuration key isn't found
	 * @return mixed Configuration value
	 */
	function aiconfig( $key, $default = null )
	{
		return app( 'aimeos.config' )->get()->get( $key, $default );
	}
}


if( !function_exists( 'aitrans' ) )
{
	/**
	 * Translates the given message
	 *
	 * @param string $singular Message to translate
	 * @param array $params List of paramters for replacing the placeholders in that order
	 * @param string $domain Translation domain
	 * @param string $locale ISO language code, maybe combine with ISO currency code, e.g. "en_US"
	 * @return string Translated string
	 */
	function aitrans( $singular, array $params = array(), $domain = 'client', $locale = null )
	{
		$i18n = app( 'aimeos.context' )->get()->i18n( $locale );

		return vsprintf( $i18n->dt( $domain, $singular ), $params );
	}
}


if( !function_exists( 'aitransplural' ) )
{
	/**
	 * Translates the given messages based on the number
	 *
	 * @param string $singular Message to translate
	 * @param string $plural Message for plural translations
	 * @param integer $number Count of items to chose the correct plural translation
	 * @param array $params List of paramters for replacing the placeholders in that order
	 * @param string $domain Translation domain
	 * @param string $locale ISO language code, maybe combine with ISO currency code, e.g. "en_US"
	 * @return string Translated string
	 */
	function aitransplural( $singular, $plural, $number, array $params = array(), $domain = 'client', $locale = null )
	{
		$i18n = app( 'aimeos.context' )->get()->i18n( $locale );

		return vsprintf( $i18n->dn( $domain, $singular, $plural, $number ), $params );
	}
}


================================================
FILE: tests/AimeosTestAbstract.php
================================================
<?php

abstract class AimeosTestAbstract extends Orchestra\Testbench\BrowserKit\TestCase
{
	protected function getEnvironmentSetUp( $app )
	{
		putenv( 'APP_DEBUG=1' );

		$app['config']->set( 'app.key', 'SomeRandomStringWith32Characters' );
		$app['config']->set( 'app.cipher', 'AES-256-CBC' );

		$app['config']->set( 'database.default', 'mysql' );
		$app['config']->set( 'database.connections.mysql', [
			'driver' => 'mysql',
			'host' => env( 'DB_HOST', '127.0.0.1' ),
			'port' => env( 'DB_PORT', '3306' ),
			'database' => env( 'DB_DATABASE', 'laravel' ),
			'username' => env( 'DB_USERNAME', 'aimeos' ),
			'password' => env( 'DB_PASSWORD', 'aimeos' ),
			'unix_socket' => env( 'DB_SOCKET', '' ),
			'collation' => 'utf8_unicode_ci',
		] );

		$app['config']->set( 'shop.resource.db', [
			'adapter' => 'mysql',
			'host' => env( 'DB_HOST', '127.0.0.1' ),
			'database' => env( 'DB_DATABASE', 'laravel' ),
			'username' => env( 'DB_USERNAME', 'aimeos' ),
			'password' => env( 'DB_PASSWORD', 'aimeos' ),
			'stmt' => ["SET SESSION sort_buffer_size=2097144; SET SESSION sql_mode='ANSI'; SET NAMES 'utf8'"],
			'opt-persistent' => 0,
			'limit' => 3,
			'defaultTableOptions' => [
				'collate' => 'utf8_unicode_ci',
				'charset' => 'utf8',
			],
		] );

		$app['config']->set( 'shop.resource.fs', [
			'adapter' => 'Standard',
			'tempdir' => storage_path( 'tmp' ),
			'basedir' => storage_path( 'tmp' ),
			'baseurl' => '/aimeos',
		] );

		$app['config']->set( 'shop.authorize', false );
		$app['config']->set( 'shop.disableSites', false );
		$app['config']->set( 'shop.accessControl', false );
		$app['config']->set( 'shop.admin.graphql.debug', true );
		$app['config']->set( 'shop.routes.jqadm', ['prefix' => '{site}/jqadm'] );
		$app['config']->set( 'shop.routes.graphql', ['prefix' => '{site}/graphql'] );
		$app['config']->set( 'shop.routes.jsonadm', ['prefix' => '{site}/jsonadm'] );
		$app['config']->set( 'shop.routes.jsonapi', ['prefix' => '{site}/jsonapi'] );
		$app['config']->set( 'shop.routes.account', ['prefix' => '{site}/profile'] );
		$app['config']->set( 'shop.routes.default', ['prefix' => '{site}/shop'] );
		$app['config']->set( 'shop.routes.confirm', ['prefix' => '{site}/shop'] );
		$app['config']->set( 'shop.routes.update', ['prefix' => '{site}'] );
		$app['config']->set( 'shop.routes.page', ['prefix' => '{site}/p'] );
		$app['config']->set( 'shop.routes.resolve', ['prefix' => '{site}', 'middleware' => ['web']] );
		$app['config']->set( 'shop.routes.login', [] );
		$app['config']->set( 'shop.mshop.locale.site', 'unittest' );
		$app['config']->set( 'shop.resource.email.from-email', 'root@localhost' );

		Route::any( 'login', ['as' => 'login'] );
		Route::any( 'logout', ['as' => 'logout'] );
		Route::match( array( 'GET', 'POST' ), '{site}/resolve/{path?}', array(
			'as' => 'aimeos_resolve',
			'uses' => 'Aimeos\Shop\Controller\ResolveController@indexAction'
		) )->where( ['locale' => '[a-z]{2}(\_[A-Z]{2})?', 'site' => '[A-Za-z0-9\.\-]+', 'path', '.*'] );
	}


	protected function getPackageProviders( $app )
	{
		return ['Aimeos\Shop\ShopServiceProvider'];
	}
}

================================================
FILE: tests/Base/AimeosTest.php
================================================
<?php

class AimeosTest extends AimeosTestAbstract
{
	public function testGet()
	{
		$object = $this->app->make( '\Aimeos\Shop\Base\Aimeos' )->get();
		$this->assertInstanceOf( '\Aimeos\Bootstrap', $object );
	}


	public function testGetVersion()
	{
		$object = $this->app->make( '\Aimeos\Shop\Base\Aimeos' );
		$this->assertIsString( $object->getVersion() );
	}
}


================================================
FILE: tests/Base/ConfigTest.php
================================================
<?php

class ConfigTest extends AimeosTestAbstract
{
	public function testGet()
	{
		$aimeos = $this->app->make( '\Aimeos\Shop\Base\Aimeos' );

		$configMock = $this->getMockBuilder( '\Illuminate\Config\Repository' )
			->onlyMethods( array( 'get' ) )->getMock();

		$configMock->expects( $this->exactly( 4 ) )->method( 'get' )
			->willReturnOnConsecutiveCalls( true, 'laravel:', array(), array() );

		$object = new \Aimeos\Shop\Base\Config( $configMock, $aimeos );

		$this->assertInstanceOf( '\Aimeos\Base\Config\Iface', $object->get() );
	}
}


================================================
FILE: tests/Base/ContextTest.php
================================================
<?php

class ContextTest extends AimeosTestAbstract
{
	public function testGetNoLocale()
	{
		$session = $this->getMockBuilder( '\Illuminate\Session\Store' )->disableOriginalConstructor()->getMock();
		$config = $this->app->make( '\Aimeos\Shop\Base\Config' );
		$locale = $this->app->make( '\Aimeos\Shop\Base\Locale' );
		$i18n = $this->app->make( '\Aimeos\Shop\Base\I18n' );

		$object = new \Aimeos\Shop\Base\Context( $session, $config, $locale, $i18n );
		$ctx = $object->get( false );

		$this->assertInstanceOf( '\Aimeos\MShop\ContextIface', $ctx );
		$this->assertIsArray( $ctx->groups() );
	}
}


================================================
FILE: tests/Base/I18nTest.php
================================================
<?php

class I18nTest extends AimeosTestAbstract
{
	public function testGet()
	{
		$aimeos = $this->app->make( '\Aimeos\Shop\Base\Aimeos' );

		$configMock = $this->getMockBuilder( '\Illuminate\Config\Repository' )
			->onlyMethods( array( 'get', 'has' ) )->getMock();

		$configMock->expects( $this->once() )->method( 'has' )
			->willReturn( true );

		$configMock->expects( $this->exactly( 3 ) )->method( 'get' )
			->willReturnOnConsecutiveCalls( true, 'laravel:', array() );

		$object = new \Aimeos\Shop\Base\I18n( $configMock, $aimeos );
		$list = $object->get( array( 'en' ) );

		$this->assertInstanceOf( '\Aimeos\Base\Translation\Iface', $list['en'] );
	}
}


================================================
FILE: tests/Base/LocaleTest.php
================================================
<?php

class LocaleTest extends AimeosTestAbstract
{
	public function testGetBackend()
	{
		$mock = $this->getMockBuilder( '\Illuminate\Config\Repository' )->getMock();
		$context = $this->app->make( '\Aimeos\Shop\Base\Context' )->get( false, 'backend' );

		$object = new \Aimeos\Shop\Base\Locale( $mock );

		$this->assertInstanceOf( '\Aimeos\MShop\Locale\Item\Iface', $object->getBackend( $context, 'unittest' ) );
	}
}


================================================
FILE: tests/Base/SupportTest.php
================================================
<?php

class SupportTest extends AimeosTestAbstract
{
	public function testCheckUserGroup()
	{
		$context = $this->app->make( '\Aimeos\Shop\Base\Context' );
		$locale = $this->app->make( '\Aimeos\Shop\Base\Locale' );

		$object = new \Aimeos\Shop\Base\Support( $context, $locale );
		$user = new \Illuminate\Foundation\Auth\User();
		$user->siteid = '0';

		$this->assertFalse( $object->checkUserGroup( $user, 'admin' ) );
	}
}


================================================
FILE: tests/Base/ViewTest.php
================================================
<?php


class ViewTest extends AimeosTestAbstract
{
	public function testCreateNoLocale()
	{
		$config = $this->getMockBuilder( '\Illuminate\Config\Repository' )->getMock();

		$i18n = $this->getMockBuilder( '\Aimeos\Shop\Base\I18n' )
			->disableOriginalConstructor()
			->getMock();

		$support = $this->getMockBuilder( '\Aimeos\Shop\Base\Support' )
			->disableOriginalConstructor()
			->getMock();

		$context = new \Aimeos\MShop\Context();
		$context->setConfig( new \Aimeos\Base\Config\PHPArray() );
		$context->setSession( new \Aimeos\Base\Session\None() );

		$object = new \Aimeos\Shop\Base\View( $config, $i18n, $support );

		$this->assertInstanceOf( '\Aimeos\Base\View\Iface', $object->create( $context, array() ) );
	}
}


================================================
FILE: tests/Command/AccountCommandTest.php
================================================
<?php

class AccountCommandTest extends AimeosTestAbstract
{
	public function testAccountCommandNew()
	{
		$args = array( 'site' => 'unittest', 'email' => 'unitCustomer@example.com', '--password' => 'test' );
		$this->assertEquals( 0, $this->artisan( 'aimeos:account', $args ) );
	}


	public function testAccountCommandAdmin()
	{
		$args = array( 'site' => 'unittest', 'email' => 'unitCustomer@example.com', '--password' => 'test', '--admin' => true );
		$this->assertEquals( 0, $this->artisan( 'aimeos:account', $args ) );
	}


	public function testAccountCommandEditor()
	{
		$args = array( 'site' => 'unittest', 'email' => 'unitCustomer@example.com', '--password' => 'test', '--editor' => true );
		$this->assertEquals( 0, $this->artisan( 'aimeos:account', $args ) );
	}
}


================================================
FILE: tests/Command/ClearCommandTest.php
================================================
<?php

class ClearCommandTest extends AimeosTestAbstract
{
	public function testSetupCommand()
	{
		$this->assertEquals( 0, $this->artisan( 'aimeos:clear' ) );
	}
}


================================================
FILE: tests/Command/JobsCommandTest.php
================================================
<?php

class JobsCommandTest extends AimeosTestAbstract
{
	public function testJobsCommand()
	{
		$this->assertEquals( 0, $this->artisan( 'aimeos:jobs', array( 'jobs' => 'customer/email/watch', 'site' => 'unittest' ) ) );
	}
}


================================================
FILE: tests/Command/SetupCommandTest.php
================================================
<?php

class SetupCommandTest extends AimeosTestAbstract
{
	public function testSetupCommand()
	{
		$args = array( 'site' => 'unittest', 'tplsite' => 'unittest', '--option' => 'setup/default/demo:0' );
		$this->assertEquals( 0, $this->artisan( 'aimeos:setup', $args ) );
	}
}


================================================
FILE: tests/Controller/AccountControllerTest.php
================================================
<?php

class AccountControllerTest extends AimeosTestAbstract
{
	public function testActions()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\AccountController@indexAction', ['site' => 'unittest'] );

		$this->assertResponseOk();
		$this->assertStringContainsString( '<div class="section aimeos account-profile"', $response->getContent() );
	}


	public function testDownload()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\AccountController@downloadAction', ['site' => 'unittest', 'dl_id' => 0] );

		$this->assertEquals( 401, $response->getStatusCode() );
	}
}

================================================
FILE: tests/Controller/AdminControllerTest.php
================================================
<?php

class AdminControllerTest extends AimeosTestAbstract
{
	public function testIndexAction()
	{
		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\AdminController@indexAction' );

		$this->assertEquals( '302', $response->getStatusCode() );
	}
}

================================================
FILE: tests/Controller/BasketControllerTest.php
================================================
<?php

class BasketControllerTest extends AimeosTestAbstract
{
	public function testActions()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\BasketController@indexAction', ['site' => 'unittest'] );

		$this->assertResponseOk();
		$this->assertStringContainsString( '<div class="section aimeos basket-standard"', $response->getContent() );
		$this->assertStringContainsString( '<div class="section aimeos basket-related"', $response->getContent() );
	}
}

================================================
FILE: tests/Controller/CatalogControllerTest.php
================================================
<?php

class CatalogControllerTest extends AimeosTestAbstract
{
	public function testCountAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\CatalogController@countAction', ['site' => 'unittest'] );

		$this->assertResponseOk();
		$this->assertStringStartsWith( '{"', $response->getContent() );
	}


	public function testDetailAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\CatalogController@detailAction', ['site' => 'unittest', 'd_name' => 'Cafe_Noire_Cappuccino'] );

		$this->assertResponseOk();
		$this->assertStringContainsString( '<div class="section aimeos catalog-stage', $response->getContent() );
		$this->assertStringContainsString( '<div class="aimeos catalog-detail', $response->getContent() );
	}


	public function testListAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\CatalogController@listAction', ['site' => 'unittest'] );

		$this->assertResponseOk();
		$this->assertStringContainsString( '<div class="section aimeos catalog-filter', $response->getContent() );
		$this->assertStringContainsString( '<div class="section aimeos catalog-list', $response->getContent() );
	}


	public function testSessionAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\CatalogController@sessionAction', ['site' => 'unittest'] );

		$this->assertResponseOk();
		$this->assertStringContainsString( '<div class="section aimeos catalog-session', $response->getContent() );
		$this->assertStringContainsString( '<div class="section catalog-session-pinned', $response->getContent() );
		$this->assertStringContainsString( '<div class="section catalog-session-seen', $response->getContent() );
	}


	public function testStockAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\CatalogController@stockAction', ['site' => 'unittest'] );

		$this->assertResponseOk();
		$this->assertStringContainsString( '.aimeos .product .stock', $response->getContent() );
	}


	public function testSuggestAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\CatalogController@suggestAction', ['site' => 'unittest'], ['f_search' => 'Cafe'] );

		$this->assertResponseOk();
		$this->assertMatchesRegularExpression( '/[{.*}]/', $response->getContent() );
	}


	public function testTreeAction()
	{
		$root = \Aimeos\Controller\Frontend::create( app( 'aimeos.context' )->get(), 'catalog' )->getTree( \Aimeos\Controller\Frontend\Catalog\Iface::TREE );

		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\CatalogController@treeAction', ['site' => 'unittest', 'f_catid' => $root->getId(), 'f_name' => 'test'] );

		$this->assertResponseOk();
		$this->assertStringContainsString( '<div class="section aimeos catalog-filter', $response->getContent() );
		$this->assertStringContainsString( '<div class="section aimeos catalog-list', $response->getContent() );
	}
}

================================================
FILE: tests/Controller/CheckoutControllerTest.php
================================================
<?php

class CheckoutControllerTest extends AimeosTestAbstract
{
	public function testConfirmAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\CheckoutController@confirmAction', ['site' => 'unittest'] );

		$this->assertResponseOk();
	}


	public function testIndexAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\CheckoutController@indexAction', ['site' => 'unittest'] );

		$this->assertResponseOk();
		$this->assertStringContainsString( '<div class="section checkout-standard-address"', $response->getContent() );
	}


	public function testUpdateAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\CheckoutController@updateAction', ['site' => 'unittest'], ['code' => 'paypalexpress'] );

		$this->assertResponseOk();
		$this->assertEquals( '', $response->getContent() );
	}
}

================================================
FILE: tests/Controller/GraphqlControllerTest.php
================================================
<?php

class GraphqlControllerTest extends AimeosTestAbstract
{
	public function testQuery()
	{
		$params = ['site' => 'unittest'];
		$body = '{"query":"query {\n  findProduct(code: \"CNC\") {\n    id\n    code\n  }\n}\n","variables":{},"operationName":null}';
		$response = $this->action( 'POST', '\Aimeos\Shop\Controller\GraphqlController@indexAction', $params, [], [], [], [], $body );

		$json = json_decode( $response->getContent(), true );

		$this->assertResponseOk();
		$this->assertNotNull( $json );
	}
}


================================================
FILE: tests/Controller/JqadmControllerTest.php
================================================
<?php

class JqadmControllerTest extends AimeosTestAbstract
{
	public function testFileActionCss()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\JqadmController@fileAction', ['site' => 'unittest', 'name' => 'index-css', 'locale' => 'en'] );

		$this->assertResponseOk();
		$this->assertStringContainsString( '.aimeos', $response->getContent() );
	}


	public function testFileActionJs()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\JqadmController@fileAction', ['site' => 'unittest', 'name' => 'index-js', 'locale' => 'en'] );

		$this->assertResponseOk();
		$this->assertStringContainsString( 'Aimeos = {', $response->getContent() );
	}


	public function testBatchAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$params = ['site' => 'unittest', 'resource' => 'product', 'id' => ['0', '1']];
		$response = $this->action( 'POST', '\Aimeos\Shop\Controller\JqadmController@batchAction', $params );

		$this->assertEquals( 302, $response->getStatusCode() );
	}


	public function testCopyAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$params = ['site' => 'unittest', 'resource' => 'product', 'id' => '0'];
		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\JqadmController@copyAction', $params );

		$this->assertEquals( 200, $response->getStatusCode() );
		$this->assertStringContainsString( 'item-product', $response->getContent() );
	}


	public function testCreateAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$params = ['site' => 'unittest', 'resource' => 'product'];
		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\JqadmController@createAction', $params );

		$this->assertEquals( 200, $response->getStatusCode() );
		$this->assertStringContainsString( 'list-items', $response->getContent() );
	}


	public function testDeleteAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );
		$this->app['session']->setPreviousUrl( 'http://localhost/unittest' );

		$params = ['site' => 'unittest', 'resource' => 'product', 'id' => '0'];
		$response = $this->action( 'POST', '\Aimeos\Shop\Controller\JqadmController@deleteAction', $params );

		$this->assertEquals( 302, $response->getStatusCode() );
	}


	public function testExportAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$params = ['site' => 'unittest', 'resource' => 'order'];
		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\JqadmController@exportAction', $params );

		$this->assertEquals( 200, $response->getStatusCode() );
		$this->assertStringContainsString( 'list-items', $response->getContent() );
	}


	public function testGetAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$params = ['site' => 'unittest', 'resource' => 'product', 'id' => '0'];
		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\JqadmController@getAction', $params );

		$this->assertEquals( 200, $response->getStatusCode() );
		$this->assertStringContainsString( 'item-product', $response->getContent() );
	}


	public function testSaveAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );
		$this->app['session']->setPreviousUrl( 'http://localhost/unittest' );

		$params = ['site' => 'unittest', 'resource' => 'product', 'item' => ['product.code' => 'jqadmSaveTest']];
		$response = $this->action( 'POST', '\Aimeos\Shop\Controller\JqadmController@saveAction', $params );

		$this->assertEquals( 302, $response->getStatusCode() );
	}


	public function testSearchAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$params = ['site' => 'unittest', 'resource' => 'product'];
		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\JqadmController@searchAction', $params );

		$this->assertEquals( 200, $response->getStatusCode() );
		$this->assertStringContainsString( 'list-items', $response->getContent() );
	}


	public function testSearchActionSite()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$params = ['site' => 'invalid', 'resource' => 'product'];
		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\JqadmController@searchAction', $params );

		$this->assertEquals( 500, $response->getStatusCode() );
	}
}


================================================
FILE: tests/Controller/JsonadmControllerTest.php
================================================
<?php

class JsonadmControllerTest extends AimeosTestAbstract
{
	public function testOptionsAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );
		$this->app['session']->setPreviousUrl( 'http://localhost/unittest' );

		$params = ['site' => 'unittest', 'resource' => 'product'];
		$response = $this->action( 'OPTIONS', '\Aimeos\Shop\Controller\JsonadmController@optionsAction', $params );

		$json = json_decode( $response->getContent(), true );

		$this->assertNotNull( $json );
		$this->assertEquals( 200, $response->getStatusCode() );
		$this->assertArrayHasKey( 'resources', $json['meta'] );
		$this->assertGreaterThan( 1, count( $json['meta']['resources'] ) );


		$params = ['site' => 'unittest'];
		$response = $this->action( 'OPTIONS', '\Aimeos\Shop\Controller\JsonadmController@optionsAction', $params );

		$json = json_decode( $response->getContent(), true );

		$this->assertNotNull( $json );
		$this->assertEquals( 200, $response->getStatusCode() );
		$this->assertArrayHasKey( 'resources', $json['meta'] );
		$this->assertGreaterThan( 1, count( $json['meta']['resources'] ) );
	}


	public function testActionsSingle()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );
		$this->app['session']->setPreviousUrl( 'http://localhost/unittest' );

		$params = ['site' => 'unittest', 'resource' => 'stock/type'];
		$content = '{"data":{"type":"stock/type","attributes":{"stock.type.code":"laravel","stock.type.label":"laravel"}}}';
		$response = $this->action( 'POST', '\Aimeos\Shop\Controller\JsonadmController@postAction', $params, [], [], [], [], $content );

		$json = json_decode( $response->getContent(), true );

		$this->assertEquals( 201, $response->getStatusCode() );
		$this->assertNotNull( $json );
		$this->assertArrayHasKey( 'stock.type.id', $json['data']['attributes'] );
		$this->assertEquals( 'laravel', $json['data']['attributes']['stock.type.code'] );
		$this->assertEquals( 'laravel', $json['data']['attributes']['stock.type.label'] );
		$this->assertEquals( 1, $json['meta']['total'] );

		$id = $json['data']['attributes']['stock.type.id'];


		$params = ['site' => 'unittest', 'resource' => 'stock/type', 'id' => $id];
		$content = '{"data":{"type":"stock/type","attributes":{"stock.type.code":"laravel2","stock.type.label":"laravel2"}}}';
		$response = $this->action( 'PATCH', '\Aimeos\Shop\Controller\JsonadmController@patchAction', $params, [], [], [], [], $content );

		$json = json_decode( $response->getContent(), true );

		$this->assertResponseOk();
		$this->assertNotNull( $json );
		$this->assertArrayHasKey( 'stock.type.id', $json['data']['attributes'] );
		$this->assertEquals( 'laravel2', $json['data']['attributes']['stock.type.code'] );
		$this->assertEquals( 'laravel2', $json['data']['attributes']['stock.type.label'] );
		$this->assertEquals( $id, $json['data']['attributes']['stock.type.id'] );
		$this->assertEquals( 1, $json['meta']['total'] );


		$params = ['site' => 'unittest', 'resource' => 'stock/type', 'id' => $id];
		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\JsonadmController@getAction', $params );

		$json = json_decode( $response->getContent(), true );

		$this->assertResponseOk();
		$this->assertNotNull( $json );
		$this->assertArrayHasKey( 'stock.type.id', $json['data']['attributes'] );
		$this->assertEquals( 'laravel2', $json['data']['attributes']['stock.type.code'] );
		$this->assertEquals( 'laravel2', $json['data']['attributes']['stock.type.label'] );
		$this->assertEquals( $id, $json['data']['attributes']['stock.type.id'] );
		$this->assertEquals( 1, $json['meta']['total'] );


		$params = ['site' => 'unittest', 'resource' => 'stock/type', 'id' => $id];
		$response = $this->action( 'DELETE', '\Aimeos\Shop\Controller\JsonadmController@deleteAction', $params );

		$json = json_decode( $response->getContent(), true );

		$this->assertResponseOk();
		$this->assertNotNull( $json );
		$this->assertEquals( 1, $json['meta']['total'] );
	}


	public function testActionsBulk()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );
		$this->app['session']->setPreviousUrl( 'http://localhost/unittest' );

		$params = ['site' => 'unittest', 'resource' => 'stock/type'];
		$content = '{"data":[
			{"type":"stock/type","attributes":{"stock.type.code":"laravel","stock.type.label":"laravel"}},
			{"type":"stock/type","attributes":{"stock.type.code":"laravel2","stock.type.label":"laravel"}}
		]}';
		$response = $this->action( 'POST', '\Aimeos\Shop\Controller\JsonadmController@postAction', $params, [], [], [], [], $content );

		$json = json_decode( $response->getContent(), true );

		$this->assertEquals( 201, $response->getStatusCode() );
		$this->assertNotNull( $json );
		$this->assertEquals( 2, count( $json['data'] ) );
		$this->assertArrayHasKey( 'stock.type.id', $json['data'][0]['attributes'] );
		$this->assertArrayHasKey( 'stock.type.id', $json['data'][1]['attributes'] );
		$this->assertEquals( 'laravel', $json['data'][0]['attributes']['stock.type.label'] );
		$this->assertEquals( 'laravel', $json['data'][1]['attributes']['stock.type.label'] );
		$this->assertEquals( 2, $json['meta']['total'] );

		$ids = array( $json['data'][0]['attributes']['stock.type.id'], $json['data'][1]['attributes']['stock.type.id'] );


		$params = ['site' => 'unittest', 'resource' => 'stock/type'];
		$content = '{"data":[
			{"type":"stock/type","id":' . $ids[0] . ',"attributes":{"stock.type.label":"laravel2"}},
			{"type":"stock/type","id":' . $ids[1] . ',"attributes":{"stock.type.label":"laravel2"}}
		]}';
		$response = $this->action( 'PATCH', '\Aimeos\Shop\Controller\JsonadmController@patchAction', $params, [], [], [], [], $content );

		$json = json_decode( $response->getContent(), true );

		$this->assertResponseOk();
		$this->assertNotNull( $json );
		$this->assertEquals( 2, count( $json['data'] ) );
		$this->assertArrayHasKey( 'stock.type.id', $json['data'][0]['attributes'] );
		$this->assertArrayHasKey( 'stock.type.id', $json['data'][1]['attributes'] );
		$this->assertEquals( 'laravel2', $json['data'][0]['attributes']['stock.type.label'] );
		$this->assertEquals( 'laravel2', $json['data'][1]['attributes']['stock.type.label'] );
		$this->assertTrue( in_array( $json['data'][0]['attributes']['stock.type.id'], $ids ) );
		$this->assertTrue( in_array( $json['data'][1]['attributes']['stock.type.id'], $ids ) );
		$this->assertEquals( 2, $json['meta']['total'] );


		$params = ['site' => 'unittest', 'resource' => 'stock/type'];
		$getParams = ['filter' => ['&&' => [
			['=~' => ['stock.type.code' => 'laravel']],
			['==' => ['stock.type.label' => 'laravel2']]
			]],
			'sort' => 'stock.type.code', 'page' => ['offset' => 0, 'limit' => 3]
		];
		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\JsonadmController@getAction', $params, $getParams );

		$json = json_decode( $response->getContent(), true );

		$this->assertResponseOk();
		$this->assertNotNull( $json );
		$this->assertEquals( 2, count( $json['data'] ) );
		$this->assertEquals( 'laravel', $json['data'][0]['attributes']['stock.type.code'] );
		$this->assertEquals( 'laravel2', $json['data'][1]['attributes']['stock.type.code'] );
		$this->assertEquals( 'laravel2', $json['data'][0]['attributes']['stock.type.label'] );
		$this->assertEquals( 'laravel2', $json['data'][1]['attributes']['stock.type.label'] );
		$this->assertTrue( in_array( $json['data'][0]['attributes']['stock.type.id'], $ids ) );
		$this->assertTrue( in_array( $json['data'][1]['attributes']['stock.type.id'], $ids ) );
		$this->assertEquals( 2, $json['meta']['total'] );


		$params = ['site' => 'unittest', 'resource' => 'stock/type'];
		$content = '{"data":[
			{"type":"stock/type","id":' . $ids[0] . '},
			{"type":"stock/type","id":' . $ids[1] . '}
		]}';
		$response = $this->action( 'DELETE', '\Aimeos\Shop\Controller\JsonadmController@deleteAction', $params, [], [], [], [], $content );

		$json = json_decode( $response->getContent(), true );

		$this->assertResponseOk();
		$this->assertNotNull( $json );
		$this->assertEquals( 2, $json['meta']['total'] );
	}


	public function testPutAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );
		$this->app['session']->setPreviousUrl( 'http://localhost/unittest' );

		$params = ['site' => 'unittest', 'resource' => 'stock/type'];
		$content = '{"data":[
			{"type":"stock/type","attributes":{"stock.type.code":"laravel","stock.type.label":"laravel"}},
			{"type":"stock/type","attributes":{"stock.type.code":"laravel2","stock.type.label":"laravel"}}
		]}';
		$response = $this->action( 'PUT', '\Aimeos\Shop\Controller\JsonadmController@postAction', $params, [], [], [], [], $content );

		$json = json_decode( $response->getContent(), true );

		$this->assertEquals( 501, $response->getStatusCode() );
		$this->assertNotNull( $json );
	}
}


================================================
FILE: tests/Controller/JsonapiControllerTest.php
================================================
<?php

class JsonapiControllerTest extends AimeosTestAbstract
{
	public function testOptionsAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$params = ['site' => 'unittest'];
		$response = $this->action( 'OPTIONS', '\Aimeos\Shop\Controller\JsonapiController@optionsAction', $params );

		$json = json_decode( $response->getContent(), true );

		$this->assertResponseOk();
		$this->assertNotNull( $json );
		$this->assertArrayHasKey( 'resources', $json['meta'] );
		$this->assertGreaterThan( 1, count( $json['meta']['resources'] ) );
	}


	public function testGetAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$params = ['site' => 'unittest', 'resource' => 'product'];
		$getParams = ['filter' => ['f_search' => 'Cafe Noire Cap'], 'sort' => 'product.code'];
		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\JsonapiController@getAction', $params, $getParams );

		$json = json_decode( $response->getContent(), true );

		$this->assertResponseOk();
		$this->assertNotNull( $json );
		$this->assertEquals( 3, $json['meta']['total'] );
		$this->assertEquals( 3, count( $json['data'] ) );
		$this->assertArrayHasKey( 'id', $json['data'][0] );
		$this->assertEquals( 'CNC', $json['data'][0]['attributes']['product.code'] );

		$id = $json['data'][0]['id'];


		$params = ['site' => 'unittest', 'resource' => 'product', 'id' => $id];
		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\JsonapiController@getAction', $params );

		$json = json_decode( $response->getContent(), true );

		$this->assertResponseOk();
		$this->assertNotNull( $json );
		$this->assertEquals( 1, $json['meta']['total'] );
		$this->assertArrayHasKey( 'id', $json['data'] );
		$this->assertEquals( 'CNC', $json['data']['attributes']['product.code'] );
	}


	public function testPostPatchDeleteAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		// get CNC product
		$params = ['site' => 'unittest', 'resource' => 'product'];
		$getParams = ['filter' => ['f_search' => 'Cafe Noire Cap', 'f_listtype' => 'unittype19'], 'sort' => 'product.code'];
		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\JsonapiController@getAction', $params, $getParams );

		$this->assertResponseOk();
		$json = json_decode( $response->getContent(), true );
		$this->assertEquals( 'CNC', $json['data'][0]['attributes']['product.code'] );

		// add CNC product to basket
		$params = ['site' => 'unittest', 'resource' => 'basket', 'id' => 'default', 'related' => 'product'];
		$content = json_encode( ['data' => ['attributes' => ['product.id' => $json['data'][0]['id']]]] );
		$response = $this->action( 'POST', '\Aimeos\Shop\Controller\JsonapiController@postAction', $params, [], [], [], [], $content );

		$this->assertEquals( 201, $response->getStatusCode() );
		$json = json_decode( $response->getContent(), true );
		$this->assertEquals( 'CNC', $json['included'][0]['attributes']['order.product.prodcode'] );

		// change product quantity in basket
		$params = ['site' => 'unittest', 'resource' => 'basket', 'id' => 'default', 'related' => 'product', 'relatedid' => 0];
		$content = json_encode( ['data' => ['attributes' => ['quantity' => 2]]] );
		$response = $this->action( 'PATCH', '\Aimeos\Shop\Controller\JsonapiController@patchAction', $params, [], [], [], [], $content );

		$this->assertResponseOk();
		$json = json_decode( $response->getContent(), true );
		$this->assertEquals( 2, $json['included'][0]['attributes']['order.product.quantity'] );

		// delete product from basket
		$params = ['site' => 'unittest', 'resource' => 'basket', 'id' => 'default', 'related' => 'product', 'relatedid' => 0];
		$response = $this->action( 'DELETE', '\Aimeos\Shop\Controller\JsonapiController@deleteAction', $params );

		$this->assertResponseOk();
		$json = json_decode( $response->getContent(), true );
		$this->assertEquals( 0, count( $json['included'] ) );
	}


	public function testPutAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$params = ['site' => 'unittest', 'resource' => 'basket'];
		$response = $this->action( 'PUT', '\Aimeos\Shop\Controller\JsonapiController@putAction', $params );

		$this->assertEquals( 403, $response->getStatusCode() );
		$json = json_decode( $response->getContent(), true );
		$this->assertArrayHasKey( 'errors', $json );
	}
}


================================================
FILE: tests/Controller/PageControllerTest.php
================================================
<?php

class PageControllerTest extends AimeosTestAbstract
{
	public function testIndexAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\PageController@indexAction', ['site' => 'unittest', 'path' => 'contact'] );

		$this->assertEquals( 200, $response->getStatusCode() );
	}
}

================================================
FILE: tests/Controller/ResolveControllerTest.php
================================================
<?php

class ResolveControllerTest extends AimeosTestAbstract
{
	public function testCategory()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\ResolveController@indexAction', ['site' => 'unittest', 'path' => 'tee'] );

		$this->assertResponseOk();
		$this->assertStringContainsString( '<div class="section aimeos catalog-filter', $response->getContent() );
	}


	public function testProduct()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\ResolveController@indexAction', ['site' => 'unittest', 'path' => 'Cafe_Noire_Cappuccino'] );

		$this->assertResponseOk();
		$this->assertStringContainsString( '<div class="aimeos catalog-detail', $response->getContent() );
	}


	public function testNotFound()
	{
		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\ResolveController@indexAction', ['site' => 'unittest', 'path' => 'invalid'] );

		$response->assertStatus( 404 );
	}


	protected function getEnvironmentSetUp( $app )
	{
		parent::getEnvironmentSetUp( $app );

		$app['config']->set( 'shop.client.html.catalog.detail.url.target', 'aimeos_resolve' );
	}
}

================================================
FILE: tests/Controller/SupplierControllerTest.php
================================================
<?php

class SupplierControllerTest extends AimeosTestAbstract
{
	public function testDetailAction()
	{
		View::addLocation( dirname( __DIR__ ) . '/fixtures/views' );

		$context = app( 'aimeos.context' )->get( true );
		$item = \Aimeos\Controller\Frontend::create( $context, 'supplier' )->slice( 0, 1 )->search()->first();
		$params = ['site' => 'unittest', 's_name' => 'Test supplier', 'f_supid' => $item->getId()];

		$response = $this->action( 'GET', '\Aimeos\Shop\Controller\SupplierController@detailAction', $params );

		$this->assertResponseOk();
		$this->assertStringContainsString( '<div class="section aimeos supplier-detail', $response->getContent() );
		$this->assertStringContainsString( '<div class="section aimeos catalog-list', $response->getContent() );
	}
}

================================================
FILE: tests/FacadesTest.php
================================================
<?php


class FacadesTest extends AimeosTestAbstract
{
	public function testAttribute()
	{
		$this->assertInstanceOf( \Aimeos\Controller\Frontend\Iface::class, \Aimeos\Shop\Facades\Attribute::uses( [] ) );
	}


	public function testBasket()
	{
		$this->assertInstanceOf( \Aimeos\Controller\Frontend\Iface::class, \Aimeos\Shop\Facades\Basket::clear() );
	}


	public function testCatalog()
	{
		$this->assertInstanceOf( \Aimeos\Controller\Frontend\Iface::class, \Aimeos\Shop\Facades\Catalog::uses( [] ) );
	}


	public function testCms()
	{
		$this->assertInstanceOf( \Aimeos\Controller\Frontend\Iface::class, \Aimeos\Shop\Facades\Cms::uses( [] ) );
	}


	public function testCustomer()
	{
		$this->assertInstanceOf( \Aimeos\Controller\Frontend\Iface::class, \Aimeos\Shop\Facades\Customer::uses( [] ) );
	}


	public function testLocale()
	{
		$this->assertInstanceOf( \Aimeos\Controller\Frontend\Iface::class, \Aimeos\Shop\Facades\Locale::compare( '==', 'locale.id', -1 ) );
	}


	public function testOrder()
	{
		$this->assertInstanceOf( \Aimeos\Controller\Frontend\Iface::class, \Aimeos\Shop\Facades\Order::uses( [] ) );
	}


	public function testProduct()
	{
		$this->assertInstanceOf( \Aimeos\Controller\Frontend\Iface::class, \Aimeos\Shop\Facades\Product::uses( [] ) );
	}


	public function testService()
	{
		$this->assertInstanceOf( \Aimeos\Controller\Frontend\Iface::class, \Aimeos\Shop\Facades\Service::uses( [] ) );
	}


	public function testStock()
	{
		$this->assertInstanceOf( \Aimeos\Controller\Frontend\Iface::class, \Aimeos\Shop\Facades\Stock::compare( '==', 'stock.id', -1 ) );
	}


	public function testSubscription()
	{
		$this->assertInstanceOf( \Aimeos\Controller\Frontend\Iface::class, \Aimeos\Shop\Facades\Subscription::uses( [] ) );
	}


	public function testSupplier()
	{
		$this->assertInstanceOf( \Aimeos\Controller\Frontend\Iface::class, \Aimeos\Shop\Facades\Supplier::uses( [] ) );
	}
}


================================================
FILE: tests/HelpersTest.php
================================================
<?php


class HelpersTest extends AimeosTestAbstract
{
	public function testAiconfig()
	{
		$this->assertEquals( 'notexisting', aiconfig( 'not/exists', 'notexisting' ) );
	}
}


================================================
FILE: tests/fixtures/views/app.blade.php
================================================
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta name="csrf-token" content="{{ csrf_token() }}" />
		@yield('aimeos_header')
		<title>Laravel</title>
		@yield('aim
Download .txt
gitextract_8j091w8s/

├── .circleci/
│   └── config.yml
├── .coveralls.yml
├── .devcontainer/
│   └── devcontainer.json
├── .gitattributes
├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.md
│       └── feature_request.md
├── .gitignore
├── LICENSE
├── README.md
├── build.xml
├── composer.json
├── config/
│   ├── default.php
│   └── shop.php
├── phpunit.xml.dist
├── routes/
│   └── aimeos.php
├── src/
│   ├── Base/
│   │   ├── Aimeos.php
│   │   ├── Config.php
│   │   ├── Context.php
│   │   ├── I18n.php
│   │   ├── Locale.php
│   │   ├── Shop.php
│   │   ├── Support.php
│   │   └── View.php
│   ├── Command/
│   │   ├── AbstractCommand.php
│   │   ├── AccountCommand.php
│   │   ├── ClearCommand.php
│   │   ├── JobsCommand.php
│   │   └── SetupCommand.php
│   ├── Composer.php
│   ├── Controller/
│   │   ├── AccountController.php
│   │   ├── AdminController.php
│   │   ├── BasketController.php
│   │   ├── CatalogController.php
│   │   ├── CheckoutController.php
│   │   ├── GraphqlController.php
│   │   ├── JqadmController.php
│   │   ├── JsonadmController.php
│   │   ├── JsonapiController.php
│   │   ├── PageController.php
│   │   ├── ResolveController.php
│   │   └── SupplierController.php
│   ├── Facades/
│   │   ├── Attribute.php
│   │   ├── Basket.php
│   │   ├── Catalog.php
│   │   ├── Cms.php
│   │   ├── Customer.php
│   │   ├── Locale.php
│   │   ├── Order.php
│   │   ├── Product.php
│   │   ├── Service.php
│   │   ├── Shop.php
│   │   ├── Stock.php
│   │   ├── Subscription.php
│   │   └── Supplier.php
│   ├── ShopServiceProvider.php
│   └── helpers.php
├── tests/
│   ├── AimeosTestAbstract.php
│   ├── Base/
│   │   ├── AimeosTest.php
│   │   ├── ConfigTest.php
│   │   ├── ContextTest.php
│   │   ├── I18nTest.php
│   │   ├── LocaleTest.php
│   │   ├── SupportTest.php
│   │   └── ViewTest.php
│   ├── Command/
│   │   ├── AccountCommandTest.php
│   │   ├── ClearCommandTest.php
│   │   ├── JobsCommandTest.php
│   │   └── SetupCommandTest.php
│   ├── Controller/
│   │   ├── AccountControllerTest.php
│   │   ├── AdminControllerTest.php
│   │   ├── BasketControllerTest.php
│   │   ├── CatalogControllerTest.php
│   │   ├── CheckoutControllerTest.php
│   │   ├── GraphqlControllerTest.php
│   │   ├── JqadmControllerTest.php
│   │   ├── JsonadmControllerTest.php
│   │   ├── JsonapiControllerTest.php
│   │   ├── PageControllerTest.php
│   │   ├── ResolveControllerTest.php
│   │   └── SupplierControllerTest.php
│   ├── FacadesTest.php
│   ├── HelpersTest.php
│   └── fixtures/
│       └── views/
│           └── app.blade.php
└── views/
    ├── account/
    │   └── index.blade.php
    ├── admin/
    │   └── index.blade.php
    ├── base.blade.php
    ├── basket/
    │   └── index.blade.php
    ├── catalog/
    │   ├── count.blade.php
    │   ├── detail.blade.php
    │   ├── home.blade.php
    │   ├── list.blade.php
    │   ├── session.blade.php
    │   ├── stock.blade.php
    │   ├── suggest.blade.php
    │   └── tree.blade.php
    ├── checkout/
    │   ├── confirm.blade.php
    │   ├── index.blade.php
    │   └── update.blade.php
    ├── jqadm/
    │   └── index.blade.php
    ├── page/
    │   └── index.blade.php
    └── supplier/
        └── detail.blade.php
Download .txt
SYMBOL INDEX (259 symbols across 67 files)

FILE: src/Base/Aimeos.php
  class Aimeos (line 14) | class Aimeos
    method __construct (line 32) | public function __construct( \Illuminate\Contracts\Config\Repository $...
    method get (line 43) | public function get() : \Aimeos\Bootstrap
    method getVersion (line 66) | public function getVersion() : string

FILE: src/Base/Config.php
  class Config (line 14) | class Config
    method __construct (line 38) | public function __construct( \Illuminate\Contracts\Config\Repository $...
    method get (line 51) | public function get( string $type = 'frontend' ) : \Aimeos\Base\Config...

FILE: src/Base/Context.php
  class Context (line 19) | class Context
    method __construct (line 55) | public function __construct( \Illuminate\Session\Store $session, \Aime...
    method get (line 71) | public function get( bool $locale = true, string $type = 'frontend' ) ...
    method addCache (line 117) | protected function addCache( \Aimeos\MShop\ContextIface $context ) : \...
    method addDatabaseManager (line 131) | protected function addDatabaseManager( \Aimeos\MShop\ContextIface $con...
    method addFilesystemManager (line 145) | protected function addFilesystemManager( \Aimeos\MShop\ContextIface $c...
    method addLogger (line 160) | protected function addLogger( \Aimeos\MShop\ContextIface $context ) : ...
    method addMailer (line 175) | protected function addMailer( \Aimeos\MShop\ContextIface $context ) : ...
    method addMessageQueueManager (line 189) | protected function addMessageQueueManager( \Aimeos\MShop\ContextIface ...
    method addNonce (line 203) | protected function addNonce( \Aimeos\MShop\ContextIface $context ) : \...
    method addPassword (line 215) | protected function addPassword( \Aimeos\MShop\ContextIface $context ) ...
    method addProcess (line 227) | protected function addProcess( \Aimeos\MShop\ContextIface $context ) :...
    method addSession (line 246) | protected function addSession( \Aimeos\MShop\ContextIface $context ) :...
    method addToken (line 260) | protected function addToken( \Aimeos\MShop\ContextIface $context ) : \...
    method addUserGroups (line 276) | protected function addUserGroups( \Aimeos\MShop\ContextIface $context ...

FILE: src/Base/I18n.php
  class I18n (line 14) | class I18n
    method __construct (line 38) | public function __construct( \Illuminate\Contracts\Config\Repository $...
    method get (line 51) | public function get( array $languageIds ) : array

FILE: src/Base/Locale.php
  class Locale (line 18) | class Locale
    method __construct (line 36) | public function __construct( \Illuminate\Contracts\Config\Repository $...
    method get (line 48) | public function get( \Aimeos\MShop\ContextIface $context ) : \Aimeos\M...
    method getBackend (line 84) | public function getBackend( \Aimeos\MShop\ContextIface $context, strin...

FILE: src/Base/Shop.php
  class Shop (line 14) | class Shop
    method __construct (line 39) | public function __construct( \Aimeos\Shop\Base\Aimeos $aimeos,
    method get (line 59) | public function get( string $name ) : \Aimeos\Client\Html\Iface
    method template (line 79) | public function template( string $name ) : string
    method view (line 91) | public function view() : \Aimeos\Base\View\Iface

FILE: src/Base/Support.php
  class Support (line 18) | class Support
    method __construct (line 42) | public function __construct( \Aimeos\Shop\Base\Context $context, \Aime...
    method checkUserGroup (line 56) | public function checkUserGroup( \Illuminate\Foundation\Auth\User $user...
    method checkGroups (line 97) | protected function checkGroups( \Aimeos\MShop\ContextIface $context, s...

FILE: src/Base/View.php
  class View (line 19) | class View
    method __construct (line 44) | public function __construct( \Illuminate\Contracts\Config\Repository $...
    method create (line 61) | public function create( \Aimeos\MShop\ContextIface $context, array $te...
    method addAccess (line 92) | protected function addAccess( \Aimeos\Base\View\Iface $view, \Aimeos\M...
    method addConfig (line 121) | protected function addConfig( \Aimeos\Base\View\Iface $view, \Aimeos\B...
    method addCsrf (line 137) | protected function addCsrf( \Aimeos\Base\View\Iface $view ) : \Aimeos\...
    method addNumber (line 154) | protected function addNumber( \Aimeos\Base\View\Iface $view, \Aimeos\B...
    method addParam (line 179) | protected function addParam( \Aimeos\Base\View\Iface $view ) : \Aimeos...
    method addRequest (line 195) | protected function addRequest( \Aimeos\Base\View\Iface $view ) : \Aime...
    method addResponse (line 210) | protected function addResponse( \Aimeos\Base\View\Iface $view ) : \Aim...
    method addSession (line 226) | protected function addSession( \Aimeos\Base\View\Iface $view, \Aimeos\...
    method addTranslate (line 242) | protected function addTranslate( \Aimeos\Base\View\Iface $view, ?strin...
    method addUrl (line 267) | protected function addUrl( \Aimeos\Base\View\Iface $view ) : \Aimeos\B...

FILE: src/Command/AbstractCommand.php
  class AbstractCommand (line 17) | abstract class AbstractCommand extends Command
    method addConfig (line 24) | protected function addConfig( \Aimeos\MShop\ContextIface $ctx ) : \Aim...
    method exec (line 45) | protected function exec( \Aimeos\MShop\ContextIface $context, \Closure...

FILE: src/Command/AccountCommand.php
  class AccountCommand (line 18) | class AccountCommand extends AbstractCommand
    method handle (line 47) | public function handle()
    method addGroups (line 95) | protected function addGroups( \Aimeos\MShop\ContextIface $context,
    method addGroup (line 117) | protected function addGroup( \Aimeos\MShop\ContextIface $context, \Aim...
    method getGroupItem (line 136) | protected function getGroupItem( \Aimeos\MShop\ContextIface $context, ...

FILE: src/Command/ClearCommand.php
  class ClearCommand (line 15) | class ClearCommand extends AbstractCommand
    method handle (line 37) | public function handle()

FILE: src/Command/JobsCommand.php
  class JobsCommand (line 17) | class JobsCommand extends AbstractCommand
    method handle (line 43) | public function handle()
    method context (line 75) | protected function context() : \Aimeos\MShop\ContextIface

FILE: src/Command/SetupCommand.php
  class SetupCommand (line 18) | class SetupCommand extends AbstractCommand
    method handle (line 46) | public function handle()

FILE: src/Composer.php
  class Composer (line 15) | class Composer
    method join (line 21) | public static function join( \Composer\Script\Event $event )

FILE: src/Controller/AccountController.php
  class AccountController (line 19) | class AccountController extends Controller
    method indexAction (line 26) | public function indexAction()
    method downloadAction (line 46) | public function downloadAction()

FILE: src/Controller/AdminController.php
  class AdminController (line 22) | class AdminController extends Controller
    method indexAction (line 33) | public function indexAction( \Illuminate\Http\Request $request )

FILE: src/Controller/BasketController.php
  class BasketController (line 19) | class BasketController extends Controller
    method indexAction (line 26) | public function indexAction()

FILE: src/Controller/CatalogController.php
  class CatalogController (line 19) | class CatalogController extends Controller
    method countAction (line 26) | public function countAction()
    method detailAction (line 47) | public function detailAction()
    method homeAction (line 75) | public function homeAction()
    method listAction (line 95) | public function listAction()
    method sessionAction (line 123) | public function sessionAction()
    method stockAction (line 143) | public function stockAction()
    method suggestAction (line 164) | public function suggestAction()
    method treeAction (line 185) | public function treeAction()

FILE: src/Controller/CheckoutController.php
  class CheckoutController (line 19) | class CheckoutController extends Controller
    method confirmAction (line 26) | public function confirmAction()
    method indexAction (line 46) | public function indexAction()
    method updateAction (line 66) | public function updateAction()

FILE: src/Controller/GraphqlController.php
  class GraphqlController (line 21) | class GraphqlController extends Controller
    method indexAction (line 32) | public function indexAction( ServerRequestInterface $request )

FILE: src/Controller/JqadmController.php
  class JqadmController (line 20) | class JqadmController extends AdminController
    method fileAction (line 30) | public function fileAction()
    method batchAction (line 64) | public function batchAction()
    method copyAction (line 85) | public function copyAction()
    method createAction (line 106) | public function createAction()
    method deleteAction (line 127) | public function deleteAction()
    method exportAction (line 148) | public function exportAction()
    method getAction (line 169) | public function getAction()
    method importAction (line 190) | public function importAction()
    method saveAction (line 211) | public function saveAction()
    method searchAction (line 232) | public function searchAction()
    method createAdmin (line 253) | protected function createAdmin() : \Aimeos\Admin\JQAdm\Iface
    method getHtml (line 287) | protected function getHtml( string $content )

FILE: src/Controller/JsonadmController.php
  class JsonadmController (line 22) | class JsonadmController extends Controller
    method deleteAction (line 33) | public function deleteAction( ServerRequestInterface $request )
    method getAction (line 49) | public function getAction( ServerRequestInterface $request )
    method patchAction (line 65) | public function patchAction( ServerRequestInterface $request )
    method postAction (line 81) | public function postAction( ServerRequestInterface $request )
    method putAction (line 97) | public function putAction( ServerRequestInterface $request )
    method optionsAction (line 113) | public function optionsAction( ServerRequestInterface $request )
    method createAdmin (line 128) | protected function createAdmin() : \Aimeos\Admin\JsonAdm\Iface

FILE: src/Controller/JsonapiController.php
  class JsonapiController (line 21) | class JsonapiController extends Controller
    method deleteAction (line 29) | public function deleteAction( ServerRequestInterface $request )
    method getAction (line 41) | public function getAction( ServerRequestInterface $request )
    method patchAction (line 53) | public function patchAction( ServerRequestInterface $request )
    method postAction (line 65) | public function postAction( ServerRequestInterface $request )
    method putAction (line 77) | public function putAction( ServerRequestInterface $request )
    method optionsAction (line 89) | public function optionsAction( ServerRequestInterface $request )
    method createClient (line 103) | protected function createClient() : \Aimeos\Client\JsonApi\Iface

FILE: src/Controller/PageController.php
  class PageController (line 20) | class PageController extends Controller
    method indexAction (line 27) | public function indexAction()

FILE: src/Controller/ResolveController.php
  class ResolveController (line 22) | class ResolveController extends Controller
    method register (line 33) | public static function register( string $name, \Closure $fcn )
    method __construct (line 42) | public function __construct()
    method indexAction (line 60) | public function indexAction( \Illuminate\Http\Request $request )
    method catalog (line 88) | protected function catalog( \Aimeos\MShop\ContextIface $context, strin...
    method cms (line 119) | protected function cms( \Aimeos\MShop\ContextIface $context, string $p...
    method product (line 150) | protected function product( \Aimeos\MShop\ContextIface $context, strin...

FILE: src/Controller/SupplierController.php
  class SupplierController (line 19) | class SupplierController extends Controller
    method detailAction (line 26) | public function detailAction()

FILE: src/Facades/Attribute.php
  class Attribute (line 15) | class Attribute extends \Illuminate\Support\Facades\Facade
    method getFacadeAccessor (line 22) | protected static function getFacadeAccessor()

FILE: src/Facades/Basket.php
  class Basket (line 15) | class Basket extends \Illuminate\Support\Facades\Facade
    method getFacadeAccessor (line 22) | protected static function getFacadeAccessor()

FILE: src/Facades/Catalog.php
  class Catalog (line 15) | class Catalog extends \Illuminate\Support\Facades\Facade
    method getFacadeAccessor (line 22) | protected static function getFacadeAccessor()

FILE: src/Facades/Cms.php
  class Cms (line 15) | class Cms extends \Illuminate\Support\Facades\Facade
    method getFacadeAccessor (line 22) | protected static function getFacadeAccessor()

FILE: src/Facades/Customer.php
  class Customer (line 15) | class Customer extends \Illuminate\Support\Facades\Facade
    method getFacadeAccessor (line 22) | protected static function getFacadeAccessor()

FILE: src/Facades/Locale.php
  class Locale (line 15) | class Locale extends \Illuminate\Support\Facades\Facade
    method getFacadeAccessor (line 22) | protected static function getFacadeAccessor()

FILE: src/Facades/Order.php
  class Order (line 15) | class Order extends \Illuminate\Support\Facades\Facade
    method getFacadeAccessor (line 22) | protected static function getFacadeAccessor()

FILE: src/Facades/Product.php
  class Product (line 15) | class Product extends \Illuminate\Support\Facades\Facade
    method getFacadeAccessor (line 22) | protected static function getFacadeAccessor()

FILE: src/Facades/Service.php
  class Service (line 15) | class Service extends \Illuminate\Support\Facades\Facade
    method getFacadeAccessor (line 22) | protected static function getFacadeAccessor()

FILE: src/Facades/Shop.php
  class Shop (line 17) | class Shop extends \Illuminate\Support\Facades\Facade
    method getFacadeAccessor (line 24) | protected static function getFacadeAccessor()

FILE: src/Facades/Stock.php
  class Stock (line 15) | class Stock extends \Illuminate\Support\Facades\Facade
    method getFacadeAccessor (line 22) | protected static function getFacadeAccessor()

FILE: src/Facades/Subscription.php
  class Subscription (line 15) | class Subscription extends \Illuminate\Support\Facades\Facade
    method getFacadeAccessor (line 22) | protected static function getFacadeAccessor()

FILE: src/Facades/Supplier.php
  class Supplier (line 15) | class Supplier extends \Illuminate\Support\Facades\Facade
    method getFacadeAccessor (line 22) | protected static function getFacadeAccessor()

FILE: src/ShopServiceProvider.php
  class ShopServiceProvider (line 19) | class ShopServiceProvider extends ServiceProvider {
    method boot (line 34) | public function boot()
    method register (line 63) | public function register()
    method provides (line 163) | public function provides()

FILE: src/helpers.php
  function airoute (line 19) | function airoute( $name, $parameters = [], $absolute = true )
  function aiconfig (line 44) | function aiconfig( $key, $default = null )
  function aitrans (line 62) | function aitrans( $singular, array $params = array(), $domain = 'client'...
  function aitransplural (line 84) | function aitransplural( $singular, $plural, $number, array $params = arr...

FILE: tests/AimeosTestAbstract.php
  class AimeosTestAbstract (line 3) | abstract class AimeosTestAbstract extends Orchestra\Testbench\BrowserKit...
    method getEnvironmentSetUp (line 5) | protected function getEnvironmentSetUp( $app )
    method getPackageProviders (line 73) | protected function getPackageProviders( $app )

FILE: tests/Base/AimeosTest.php
  class AimeosTest (line 3) | class AimeosTest extends AimeosTestAbstract
    method testGet (line 5) | public function testGet()
    method testGetVersion (line 12) | public function testGetVersion()

FILE: tests/Base/ConfigTest.php
  class ConfigTest (line 3) | class ConfigTest extends AimeosTestAbstract
    method testGet (line 5) | public function testGet()

FILE: tests/Base/ContextTest.php
  class ContextTest (line 3) | class ContextTest extends AimeosTestAbstract
    method testGetNoLocale (line 5) | public function testGetNoLocale()

FILE: tests/Base/I18nTest.php
  class I18nTest (line 3) | class I18nTest extends AimeosTestAbstract
    method testGet (line 5) | public function testGet()

FILE: tests/Base/LocaleTest.php
  class LocaleTest (line 3) | class LocaleTest extends AimeosTestAbstract
    method testGetBackend (line 5) | public function testGetBackend()

FILE: tests/Base/SupportTest.php
  class SupportTest (line 3) | class SupportTest extends AimeosTestAbstract
    method testCheckUserGroup (line 5) | public function testCheckUserGroup()

FILE: tests/Base/ViewTest.php
  class ViewTest (line 4) | class ViewTest extends AimeosTestAbstract
    method testCreateNoLocale (line 6) | public function testCreateNoLocale()

FILE: tests/Command/AccountCommandTest.php
  class AccountCommandTest (line 3) | class AccountCommandTest extends AimeosTestAbstract
    method testAccountCommandNew (line 5) | public function testAccountCommandNew()
    method testAccountCommandAdmin (line 12) | public function testAccountCommandAdmin()
    method testAccountCommandEditor (line 19) | public function testAccountCommandEditor()

FILE: tests/Command/ClearCommandTest.php
  class ClearCommandTest (line 3) | class ClearCommandTest extends AimeosTestAbstract
    method testSetupCommand (line 5) | public function testSetupCommand()

FILE: tests/Command/JobsCommandTest.php
  class JobsCommandTest (line 3) | class JobsCommandTest extends AimeosTestAbstract
    method testJobsCommand (line 5) | public function testJobsCommand()

FILE: tests/Command/SetupCommandTest.php
  class SetupCommandTest (line 3) | class SetupCommandTest extends AimeosTestAbstract
    method testSetupCommand (line 5) | public function testSetupCommand()

FILE: tests/Controller/AccountControllerTest.php
  class AccountControllerTest (line 3) | class AccountControllerTest extends AimeosTestAbstract
    method testActions (line 5) | public function testActions()
    method testDownload (line 16) | public function testDownload()

FILE: tests/Controller/AdminControllerTest.php
  class AdminControllerTest (line 3) | class AdminControllerTest extends AimeosTestAbstract
    method testIndexAction (line 5) | public function testIndexAction()

FILE: tests/Controller/BasketControllerTest.php
  class BasketControllerTest (line 3) | class BasketControllerTest extends AimeosTestAbstract
    method testActions (line 5) | public function testActions()

FILE: tests/Controller/CatalogControllerTest.php
  class CatalogControllerTest (line 3) | class CatalogControllerTest extends AimeosTestAbstract
    method testCountAction (line 5) | public function testCountAction()
    method testDetailAction (line 16) | public function testDetailAction()
    method testListAction (line 28) | public function testListAction()
    method testSessionAction (line 40) | public function testSessionAction()
    method testStockAction (line 53) | public function testStockAction()
    method testSuggestAction (line 64) | public function testSuggestAction()
    method testTreeAction (line 75) | public function testTreeAction()

FILE: tests/Controller/CheckoutControllerTest.php
  class CheckoutControllerTest (line 3) | class CheckoutControllerTest extends AimeosTestAbstract
    method testConfirmAction (line 5) | public function testConfirmAction()
    method testIndexAction (line 15) | public function testIndexAction()
    method testUpdateAction (line 26) | public function testUpdateAction()

FILE: tests/Controller/GraphqlControllerTest.php
  class GraphqlControllerTest (line 3) | class GraphqlControllerTest extends AimeosTestAbstract
    method testQuery (line 5) | public function testQuery()

FILE: tests/Controller/JqadmControllerTest.php
  class JqadmControllerTest (line 3) | class JqadmControllerTest extends AimeosTestAbstract
    method testFileActionCss (line 5) | public function testFileActionCss()
    method testFileActionJs (line 16) | public function testFileActionJs()
    method testBatchAction (line 27) | public function testBatchAction()
    method testCopyAction (line 38) | public function testCopyAction()
    method testCreateAction (line 50) | public function testCreateAction()
    method testDeleteAction (line 62) | public function testDeleteAction()
    method testExportAction (line 74) | public function testExportAction()
    method testGetAction (line 86) | public function testGetAction()
    method testSaveAction (line 98) | public function testSaveAction()
    method testSearchAction (line 110) | public function testSearchAction()
    method testSearchActionSite (line 122) | public function testSearchActionSite()

FILE: tests/Controller/JsonadmControllerTest.php
  class JsonadmControllerTest (line 3) | class JsonadmControllerTest extends AimeosTestAbstract
    method testOptionsAction (line 5) | public function testOptionsAction()
    method testActionsSingle (line 33) | public function testActionsSingle()
    method testActionsBulk (line 94) | public function testActionsBulk()
    method testPutAction (line 179) | public function testPutAction()

FILE: tests/Controller/JsonapiControllerTest.php
  class JsonapiControllerTest (line 3) | class JsonapiControllerTest extends AimeosTestAbstract
    method testOptionsAction (line 5) | public function testOptionsAction()
    method testGetAction (line 21) | public function testGetAction()
    method testPostPatchDeleteAction (line 54) | public function testPostPatchDeleteAction()
    method testPutAction (line 95) | public function testPutAction()

FILE: tests/Controller/PageControllerTest.php
  class PageControllerTest (line 3) | class PageControllerTest extends AimeosTestAbstract
    method testIndexAction (line 5) | public function testIndexAction()

FILE: tests/Controller/ResolveControllerTest.php
  class ResolveControllerTest (line 3) | class ResolveControllerTest extends AimeosTestAbstract
    method testCategory (line 5) | public function testCategory()
    method testProduct (line 16) | public function testProduct()
    method testNotFound (line 27) | public function testNotFound()
    method getEnvironmentSetUp (line 35) | protected function getEnvironmentSetUp( $app )

FILE: tests/Controller/SupplierControllerTest.php
  class SupplierControllerTest (line 3) | class SupplierControllerTest extends AimeosTestAbstract
    method testDetailAction (line 5) | public function testDetailAction()

FILE: tests/FacadesTest.php
  class FacadesTest (line 4) | class FacadesTest extends AimeosTestAbstract
    method testAttribute (line 6) | public function testAttribute()
    method testBasket (line 12) | public function testBasket()
    method testCatalog (line 18) | public function testCatalog()
    method testCms (line 24) | public function testCms()
    method testCustomer (line 30) | public function testCustomer()
    method testLocale (line 36) | public function testLocale()
    method testOrder (line 42) | public function testOrder()
    method testProduct (line 48) | public function testProduct()
    method testService (line 54) | public function testService()
    method testStock (line 60) | public function testStock()
    method testSubscription (line 66) | public function testSubscription()
    method testSupplier (line 72) | public function testSupplier()

FILE: tests/HelpersTest.php
  class HelpersTest (line 4) | class HelpersTest extends AimeosTestAbstract
    method testAiconfig (line 6) | public function testAiconfig()
Condensed preview — 101 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (236K chars).
[
  {
    "path": ".circleci/config.yml",
    "chars": 3885,
    "preview": "# PHP CircleCI 2.0 configuration file\n#\n# Check https://circleci.com/docs/2.0/language-php/ for more details\n#\nversion: "
  },
  {
    "path": ".coveralls.yml",
    "chars": 68,
    "preview": "src_dir: ./\njson_path: coveralls.json\ncoverage_clover: coverage.xml\n"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "chars": 82,
    "preview": "{\n  \"image\": \"mcr.microsoft.com/devcontainers/universal:2\",\n  \"features\": {\n  }\n}\n"
  },
  {
    "path": ".gitattributes",
    "chars": 157,
    "preview": "/.gitattributes export-ignore\n/.github export-ignore\n/.gitignore export-ignore\n/build.xml export-ignore\n/phpunit.xml.dis"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 585,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\n\n---\n\n**Environment**\n1. Version (e.g. 2020.10)\n2. Operat"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 471,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\n\n---\n\n**Is your feature request related to a problem?*"
  },
  {
    "path": ".gitignore",
    "chars": 91,
    "preview": "/vendor\ncomposer.lock\n.phpunit.result.cache\n.phpunit.cache\n.idea/\nnode_modules\nphpunit.xml\n"
  },
  {
    "path": "LICENSE",
    "chars": 1074,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Aimeos\n\nPermission is hereby granted, free of charge, to any person obtaining "
  },
  {
    "path": "README.md",
    "chars": 18539,
    "preview": "<a href=\"https://aimeos.org/\">\n    <img src=\"https://aimeos.org/fileadmin/template/icons/logo.png\" alt=\"Aimeos logo\" tit"
  },
  {
    "path": "build.xml",
    "chars": 943,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<project name=\"Aimeos Laravel package\" default=\"update\">\n\n\t<target name=\"update\""
  },
  {
    "path": "composer.json",
    "chars": 2165,
    "preview": "{\n    \"name\": \"aimeos/aimeos-laravel\",\n    \"description\": \"Cloud native, API first Laravel eCommerce package with integr"
  },
  {
    "path": "config/default.php",
    "chars": 6606,
    "preview": "<?php\n\nswitch( config( 'database.default', 'mysql' ) ) {\n\tcase 'pgsql': $aimeosIndexManagerName = 'PgSQL'; break;\n\tcase "
  },
  {
    "path": "config/shop.php",
    "chars": 7710,
    "preview": "<?php\n\nreturn [\n\n\t'apc_enabled' => false, // enable for maximum performance if APCu is available\n\t'apc_prefix' => 'larav"
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 1229,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" bootstrap=\"vendor/"
  },
  {
    "path": "routes/aimeos.php",
    "chars": 13207,
    "preview": "<?php\n\nif( ( $conf = config( 'shop.routes.admin', ['prefix' => 'admin', 'middleware' => ['web']] ) ) !== false ) {\n\n\tRou"
  },
  {
    "path": "src/Base/Aimeos.php",
    "chars": 1535,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespa"
  },
  {
    "path": "src/Base/Config.php",
    "chars": 1807,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespa"
  },
  {
    "path": "src/Base/Context.php",
    "chars": 8573,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespa"
  },
  {
    "path": "src/Base/I18n.php",
    "chars": 1663,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespa"
  },
  {
    "path": "src/Base/Locale.php",
    "chars": 2359,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespa"
  },
  {
    "path": "src/Base/Shop.php",
    "chars": 2143,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespa"
  },
  {
    "path": "src/Base/Support.php",
    "chars": 3355,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespa"
  },
  {
    "path": "src/Base/View.php",
    "chars": 8732,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\nnamespa"
  },
  {
    "path": "src/Command/AbstractCommand.php",
    "chars": 2363,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Command/AccountCommand.php",
    "chars": 4479,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Command/ClearCommand.php",
    "chars": 829,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Command/JobsCommand.php",
    "chars": 2349,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Command/SetupCommand.php",
    "chars": 1681,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Composer.php",
    "chars": 2364,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2020-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Controller/AccountController.php",
    "chars": 1274,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Controller/AdminController.php",
    "chars": 1755,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2014-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Controller/BasketController.php",
    "chars": 917,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Controller/CatalogController.php",
    "chars": 5658,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Controller/CheckoutController.php",
    "chars": 2058,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Controller/GraphqlController.php",
    "chars": 1522,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2022-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Controller/JqadmController.php",
    "chars": 7367,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Controller/JsonadmController.php",
    "chars": 4965,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Controller/JsonapiController.php",
    "chars": 3858,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2017-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Controller/PageController.php",
    "chars": 1052,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Controller/ResolveController.php",
    "chars": 5191,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2023\n */\n\n\nnamespace A"
  },
  {
    "path": "src/Controller/SupplierController.php",
    "chars": 1078,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Facades/Attribute.php",
    "chars": 479,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Facades/Basket.php",
    "chars": 464,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Facades/Catalog.php",
    "chars": 469,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Facades/Cms.php",
    "chars": 453,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Facades/Customer.php",
    "chars": 474,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Facades/Locale.php",
    "chars": 464,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Facades/Order.php",
    "chars": 459,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Facades/Product.php",
    "chars": 469,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Facades/Service.php",
    "chars": 469,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Facades/Shop.php",
    "chars": 450,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Facades/Stock.php",
    "chars": 459,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Facades/Subscription.php",
    "chars": 494,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/Facades/Supplier.php",
    "chars": 474,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2019-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/ShopServiceProvider.php",
    "chars": 5384,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2015-2023\n */\n\n\nnamesp"
  },
  {
    "path": "src/helpers.php",
    "chars": 2721,
    "preview": "<?php\n\n/**\n * @license MIT, http://opensource.org/licenses/MIT\n * @copyright Aimeos (aimeos.org), 2017-2023\n */\n\n\nif( !f"
  },
  {
    "path": "tests/AimeosTestAbstract.php",
    "chars": 3109,
    "preview": "<?php\n\nabstract class AimeosTestAbstract extends Orchestra\\Testbench\\BrowserKit\\TestCase\n{\n\tprotected function getEnviro"
  },
  {
    "path": "tests/Base/AimeosTest.php",
    "chars": 366,
    "preview": "<?php\n\nclass AimeosTest extends AimeosTestAbstract\n{\n\tpublic function testGet()\n\t{\n\t\t$object = $this->app->make( '\\Aimeo"
  },
  {
    "path": "tests/Base/ConfigTest.php",
    "chars": 548,
    "preview": "<?php\n\nclass ConfigTest extends AimeosTestAbstract\n{\n\tpublic function testGet()\n\t{\n\t\t$aimeos = $this->app->make( '\\Aimeo"
  },
  {
    "path": "tests/Base/ContextTest.php",
    "chars": 602,
    "preview": "<?php\n\nclass ContextTest extends AimeosTestAbstract\n{\n\tpublic function testGetNoLocale()\n\t{\n\t\t$session = $this->getMockB"
  },
  {
    "path": "tests/Base/I18nTest.php",
    "chars": 668,
    "preview": "<?php\n\nclass I18nTest extends AimeosTestAbstract\n{\n\tpublic function testGet()\n\t{\n\t\t$aimeos = $this->app->make( '\\Aimeos\\"
  },
  {
    "path": "tests/Base/LocaleTest.php",
    "chars": 423,
    "preview": "<?php\n\nclass LocaleTest extends AimeosTestAbstract\n{\n\tpublic function testGetBackend()\n\t{\n\t\t$mock = $this->getMockBuilde"
  },
  {
    "path": "tests/Base/SupportTest.php",
    "chars": 428,
    "preview": "<?php\n\nclass SupportTest extends AimeosTestAbstract\n{\n\tpublic function testCheckUserGroup()\n\t{\n\t\t$context = $this->app->"
  },
  {
    "path": "tests/Base/ViewTest.php",
    "chars": 734,
    "preview": "<?php\n\n\nclass ViewTest extends AimeosTestAbstract\n{\n\tpublic function testCreateNoLocale()\n\t{\n\t\t$config = $this->getMockB"
  },
  {
    "path": "tests/Command/AccountCommandTest.php",
    "chars": 777,
    "preview": "<?php\n\nclass AccountCommandTest extends AimeosTestAbstract\n{\n\tpublic function testAccountCommandNew()\n\t{\n\t\t$args = array"
  },
  {
    "path": "tests/Command/ClearCommandTest.php",
    "chars": 165,
    "preview": "<?php\n\nclass ClearCommandTest extends AimeosTestAbstract\n{\n\tpublic function testSetupCommand()\n\t{\n\t\t$this->assertEquals("
  },
  {
    "path": "tests/Command/JobsCommandTest.php",
    "chars": 227,
    "preview": "<?php\n\nclass JobsCommandTest extends AimeosTestAbstract\n{\n\tpublic function testJobsCommand()\n\t{\n\t\t$this->assertEquals( 0"
  },
  {
    "path": "tests/Command/SetupCommandTest.php",
    "chars": 276,
    "preview": "<?php\n\nclass SetupCommandTest extends AimeosTestAbstract\n{\n\tpublic function testSetupCommand()\n\t{\n\t\t$args = array( 'site"
  },
  {
    "path": "tests/Controller/AccountControllerTest.php",
    "chars": 725,
    "preview": "<?php\n\nclass AccountControllerTest extends AimeosTestAbstract\n{\n\tpublic function testActions()\n\t{\n\t\tView::addLocation( d"
  },
  {
    "path": "tests/Controller/AdminControllerTest.php",
    "chars": 258,
    "preview": "<?php\n\nclass AdminControllerTest extends AimeosTestAbstract\n{\n\tpublic function testIndexAction()\n\t{\n\t\t$response = $this-"
  },
  {
    "path": "tests/Controller/BasketControllerTest.php",
    "chars": 534,
    "preview": "<?php\n\nclass BasketControllerTest extends AimeosTestAbstract\n{\n\tpublic function testActions()\n\t{\n\t\tView::addLocation( di"
  },
  {
    "path": "tests/Controller/CatalogControllerTest.php",
    "chars": 3330,
    "preview": "<?php\n\nclass CatalogControllerTest extends AimeosTestAbstract\n{\n\tpublic function testCountAction()\n\t{\n\t\tView::addLocatio"
  },
  {
    "path": "tests/Controller/CheckoutControllerTest.php",
    "chars": 1038,
    "preview": "<?php\n\nclass CheckoutControllerTest extends AimeosTestAbstract\n{\n\tpublic function testConfirmAction()\n\t{\n\t\tView::addLoca"
  },
  {
    "path": "tests/Controller/GraphqlControllerTest.php",
    "chars": 514,
    "preview": "<?php\n\nclass GraphqlControllerTest extends AimeosTestAbstract\n{\n\tpublic function testQuery()\n\t{\n\t\t$params = ['site' => '"
  },
  {
    "path": "tests/Controller/JqadmControllerTest.php",
    "chars": 4416,
    "preview": "<?php\n\nclass JqadmControllerTest extends AimeosTestAbstract\n{\n\tpublic function testFileActionCss()\n\t{\n\t\tView::addLocatio"
  },
  {
    "path": "tests/Controller/JsonadmControllerTest.php",
    "chars": 8828,
    "preview": "<?php\n\nclass JsonadmControllerTest extends AimeosTestAbstract\n{\n\tpublic function testOptionsAction()\n\t{\n\t\tView::addLocat"
  },
  {
    "path": "tests/Controller/JsonapiControllerTest.php",
    "chars": 4356,
    "preview": "<?php\n\nclass JsonapiControllerTest extends AimeosTestAbstract\n{\n\tpublic function testOptionsAction()\n\t{\n\t\tView::addLocat"
  },
  {
    "path": "tests/Controller/PageControllerTest.php",
    "chars": 363,
    "preview": "<?php\n\nclass PageControllerTest extends AimeosTestAbstract\n{\n\tpublic function testIndexAction()\n\t{\n\t\tView::addLocation( "
  },
  {
    "path": "tests/Controller/ResolveControllerTest.php",
    "chars": 1230,
    "preview": "<?php\n\nclass ResolveControllerTest extends AimeosTestAbstract\n{\n\tpublic function testCategory()\n\t{\n\t\tView::addLocation( "
  },
  {
    "path": "tests/Controller/SupplierControllerTest.php",
    "chars": 776,
    "preview": "<?php\n\nclass SupplierControllerTest extends AimeosTestAbstract\n{\n\tpublic function testDetailAction()\n\t{\n\t\tView::addLocat"
  },
  {
    "path": "tests/FacadesTest.php",
    "chars": 1917,
    "preview": "<?php\n\n\nclass FacadesTest extends AimeosTestAbstract\n{\n\tpublic function testAttribute()\n\t{\n\t\t$this->assertInstanceOf( \\A"
  },
  {
    "path": "tests/HelpersTest.php",
    "chars": 176,
    "preview": "<?php\n\n\nclass HelpersTest extends AimeosTestAbstract\n{\n\tpublic function testAiconfig()\n\t{\n\t\t$this->assertEquals( 'notexi"
  },
  {
    "path": "tests/fixtures/views/app.blade.php",
    "chars": 427,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta name=\"csrf-token\" content=\"{{ csrf_token() }}\" />\n\t\t@yield('aimeos_head"
  },
  {
    "path": "views/account/index.blade.php",
    "chars": 1343,
    "preview": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<title>{{ __( 'Profile') }}</title>\n\t<?= $aiheader['locale/select'] ?"
  },
  {
    "path": "views/admin/index.blade.php",
    "chars": 1944,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\">\n\t\t<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\""
  },
  {
    "path": "views/base.blade.php",
    "chars": 7248,
    "preview": "<!DOCTYPE html>\n<html class=\"no-js\" lang=\"{{ str_replace('_', '-', app()->getLocale()) }}\" dir=\"{{ in_array(app()->getLo"
  },
  {
    "path": "views/basket/index.blade.php",
    "chars": 799,
    "preview": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<title>{{ __( 'Basket') }}</title>\n\t<meta name=\"robots\" content=\"noin"
  },
  {
    "path": "views/catalog/count.blade.php",
    "chars": 40,
    "preview": "<?php echo $aibody['catalog/count']; ?>\n"
  },
  {
    "path": "views/catalog/detail.blade.php",
    "chars": 896,
    "preview": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<?= $aiheader['locale/select'] ?? '' ?>\n\t<?= $aiheader['basket/mini']"
  },
  {
    "path": "views/catalog/home.blade.php",
    "chars": 700,
    "preview": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<?= $aiheader['locale/select'] ?? '' ?>\n\t<?= $aiheader['basket/mini']"
  },
  {
    "path": "views/catalog/list.blade.php",
    "chars": 1025,
    "preview": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<?= $aiheader['locale/select'] ?? '' ?>\n\t<?= $aiheader['basket/mini']"
  },
  {
    "path": "views/catalog/session.blade.php",
    "chars": 677,
    "preview": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<meta name=\"robots\" content=\"noindex\">\n\n\t<?= $aiheader['locale/select"
  },
  {
    "path": "views/catalog/stock.blade.php",
    "chars": 31,
    "preview": "<?= $aibody['catalog/stock'] ?>"
  },
  {
    "path": "views/catalog/suggest.blade.php",
    "chars": 33,
    "preview": "<?= $aibody['catalog/suggest'] ?>"
  },
  {
    "path": "views/catalog/tree.blade.php",
    "chars": 1100,
    "preview": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<?= $aiheader['locale/select'] ?? '' ?>\n\t<?= $aiheader['basket/mini']"
  },
  {
    "path": "views/checkout/confirm.blade.php",
    "chars": 473,
    "preview": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<title>{{ __( 'Thank you') }}</title>\n\t<?= $aiheader['checkout/confir"
  },
  {
    "path": "views/checkout/index.blade.php",
    "chars": 472,
    "preview": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<title>{{ __( 'Checkout') }}</title>\n\t<meta name=\"robots\" content=\"no"
  },
  {
    "path": "views/checkout/update.blade.php",
    "chars": 42,
    "preview": "<?php echo $aibody['checkout/update']; ?>\n"
  },
  {
    "path": "views/jqadm/index.blade.php",
    "chars": 4473,
    "preview": "<!DOCTYPE html>\n<html lang=\"{{ $locale }}\" dir=\"{{ $localeDir }}\">\n\t<head>\n\t\t<meta charset=\"utf-8\">\n\t\t<meta http-equiv=\""
  },
  {
    "path": "views/page/index.blade.php",
    "chars": 412,
    "preview": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<?= $aiheader['catalog/tree'] ?? '' ?>\n\t<?= $aiheader['basket/mini'] "
  },
  {
    "path": "views/supplier/detail.blade.php",
    "chars": 745,
    "preview": "@extends('shop::base')\n\n@section('aimeos_header')\n\t<?= $aiheader['supplier/detail'] ?>\n\t<?= $aiheader['locale/select'] ?"
  }
]

About this extraction

This page contains the full source code of the aimeos/aimeos-laravel GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 101 files (206.6 KB), approximately 66.5k tokens, and a symbol index with 259 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!