phpcbf and added .gitea workflows

This commit is contained in:
2026-01-23 20:32:22 -08:00
parent 4b45725b33
commit a033c13b8d
24 changed files with 949 additions and 919 deletions

View File

@@ -12,8 +12,8 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
php-version: ['8.2', '8.4'] php-version: ['8.2', '8.4']
db-type: ['mysql'] # db-type: ['mysql']
# db-type: ['sqlite', 'mysql', 'pgsql'] db-type: ['sqlite', 'mysql', 'pgsql']
prefer-lowest: [''] prefer-lowest: ['']
include: include:
- php-version: '8.2' - php-version: '8.2'
@@ -26,20 +26,13 @@ jobs:
env: env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: cakephp MYSQL_DATABASE: cakephp
# services: # services:
# postgres: # postgres:
# image: postgres # image: postgres
# ports: # ports:
# - 5432:5432 # - 5432:5432
# env: # env:
# POSTGRES_PASSWORD: postgres # POSTGRES_PASSWORD: postgres
# mysql8:
# image: mysql:8.0
# env:
# MYSQL_ALLOW_EMPTY_PASSWORD: yes
# MYSQL_DATABASE: test_db
# ports:
# - 3306:3306
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -47,7 +40,7 @@ jobs:
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: ${{ matrix.php-version }} php-version: ${{ matrix.php-version }}
extensions: mbstring, intl, sqlite, pdo_${{ matrix.db-type }} extensions: mbstring, intl, bcmath, sqlite, pdo_${{ matrix.db-type }}
coverage: pcov coverage: pcov
- name: Get composer cache directory - name: Get composer cache directory
@@ -94,11 +87,11 @@ jobs:
if: matrix.prefer-lowest == 'prefer-lowest' if: matrix.prefer-lowest == 'prefer-lowest'
run: vendor/bin/validate-prefer-lowest -m run: vendor/bin/validate-prefer-lowest -m
# - name: Upload coverage reports to Codecov # - name: Upload coverage reports to Codecov
# if: success() && matrix.php-version == '8.2' # if: success() && matrix.php-version == '8.2'
# uses: codecov/codecov-action@v4 # uses: codecov/codecov-action@v4
# with: # with:
# token: ${{ secrets.CODECOV_TOKEN }} # token: ${{ secrets.CODECOV_TOKEN }}
validation: validation:
name: Coding Standard & Static Analysis name: Coding Standard & Static Analysis
@@ -108,8 +101,8 @@ jobs:
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: '8.1' php-version: '8.2'
extensions: mbstring, intl, sqlite extensions: mbstring, intl, sqlite, bcmath
coverage: none coverage: none
- name: Get composer cache directory - name: Get composer cache directory
@@ -133,11 +126,8 @@ jobs:
composer install --no-progress --prefer-dist --optimize-autoloader composer install --no-progress --prefer-dist --optimize-autoloader
fi fi
- name: Composer phpstan setup
run: composer stan-setup
- name: Run phpstan - name: Run phpstan
run: vendor/bin/phpstan analyse --error-format=github run: vendor/bin/phpstan analyse src --error-format=github
- name: Run phpcs - name: Run phpcs
run: composer cs-check run: composer cs-check

View File

@@ -0,0 +1,17 @@
name: Release
on:
push:
tags:
- "v*.*.*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Release
uses: softprops/action-gh-release@v2

View File

@@ -8,11 +8,15 @@
"php": ">=8.2", "php": ">=8.2",
"dereuromark/cakephp-tools": "^3.9", "dereuromark/cakephp-tools": "^3.9",
"muffin/trash": "^4.2", "muffin/trash": "^4.2",
"cakephp/cakephp": "^5.0.1" "cakephp/cakephp": "^5.1"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^10.1", "phpunit/phpunit": "^10.1",
"cakephp/migrations": "^4.0.0" "cakephp/migrations": "^4.0.0",
"phpstan/phpstan": "^2.1",
"dereuromark/composer-prefer-lowest": "^0.1.10",
"cakedc/cakephp-phpstan": "^4.1",
"fig-r/psr2r-sniffer": "^2.7"
}, },
"suggest": { "suggest": {
"hi-powered-dev/cake-products": "Manage products and SKUs with a heirarhical category structure, includes attributes and variants" "hi-powered-dev/cake-products": "Manage products and SKUs with a heirarhical category structure, includes attributes and variants"
@@ -28,5 +32,17 @@
"Cake\\Test\\": "vendor/cakephp/cakephp/tests/", "Cake\\Test\\": "vendor/cakephp/cakephp/tests/",
"TestApp\\": "tests/test_app/src/" "TestApp\\": "tests/test_app/src/"
} }
},
"scripts": {
"cs-check": "vendor/bin/phpcs --colors --parallel=16",
"cs-fix": "vendor/bin/phpcbf --colors --parallel=16",
"lowest": "validate-prefer-lowest",
"lowest-setup": "composer update --prefer-lowest --prefer-stable --prefer-dist --no-interaction && cp composer.json composer.backup && composer require --dev dereuromark/composer-prefer-lowest && mv composer.backup composer.json",
"stan": "phpstan analyze"
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": false
}
} }
} }

View File

@@ -3,112 +3,112 @@ declare(strict_types=1);
use Migrations\BaseMigration; use Migrations\BaseMigration;
class CreateCarts extends BaseMigration class CreateCarts extends BaseMigration {
{
/** /**
* Change Method. * Change Method.
* *
* More information on this method is available here: * More information on this method is available here:
* https://book.cakephp.org/migrations/4/en/migrations.html#the-change-method * https://book.cakephp.org/migrations/4/en/migrations.html#the-change-method
* @return void * @return void
*/ */
public function change(): void public function change(): void {
{ $table = $this->table('carts', ['id' => false, 'primary_key' => ['id']]);
$table = $this->table('carts', ['id' => false, 'primary_key' => ['id']]); $table->addColumn('id', 'uuid', [
$table->addColumn('id', 'uuid', [ 'default' => null,
'default' => null, 'null' => false,
'null' => false, ]);
]); $table->addColumn('cart_type_id', 'integer', [
$table->addColumn('cart_type_id', 'integer', [ 'default' => null,
'default' => null, 'limit' => 11,
'limit' => 11, 'null' => false,
'null' => false, ]);
]); $table->addColumn('session_id', 'string', [
$table->addColumn('session_id', 'string', [ 'default' => null,
'default' => null, 'null' => true,
'null' => true, ]);
]); $table->addColumn('user_id', 'integer', [
$table->addColumn('user_id', 'integer', [ 'default' => null,
'default' => null, 'limit' => 11,
'limit' => 11, 'null' => true,
'null' => true, ]);
]); $table->addColumn('user_id_uuid', 'uuid', [
$table->addColumn('user_id_uuid', 'uuid', [ 'default' => null,
'default' => null, 'null' => true,
'null' => true, ]);
]); $table->addColumn('created', 'datetime', [
$table->addColumn('created', 'datetime', [ 'default' => null,
'default' => null, 'null' => false,
'null' => false, ]);
]); $table->addColumn('modified', 'datetime', [
$table->addColumn('modified', 'datetime', [ 'default' => null,
'default' => null, 'null' => true,
'null' => true, ]);
]); $table->addColumn('deleted', 'datetime', [
$table->addColumn('deleted', 'datetime', [ 'default' => null,
'default' => null, 'null' => true,
'null' => true, ]);
]); $table->addColumn('removed', 'datetime', [
$table->addColumn('removed', 'datetime', [ 'default' => null,
'default' => null, 'null' => true,
'null' => true, ]);
]); $table->addColumn('removed_reason_id', 'integer', [
$table->addColumn('removed_reason_id', 'integer', [ 'default' => null,
'default' => null, 'limit' => 11,
'limit' => 11, 'null' => true,
'null' => true, ]);
]); $table->addColumn('num_items', 'integer', [
$table->addColumn('num_items', 'integer', [ 'default' => 0,
'default' => 0, 'null' => false,
'null' => false, ]);
]); $table->create();
$table->create();
$table = $this->table('cart_items', ['id' => false, 'primary_key' => ['id']]); $table = $this->table('cart_items', ['id' => false, 'primary_key' => ['id']]);
$table->addColumn('id', 'uuid', [ $table->addColumn('id', 'uuid', [
'default' => null, 'default' => null,
'null' => false, 'null' => false,
]); ]);
$table->addColumn('foreign_key', 'integer', [ $table->addColumn('foreign_key', 'integer', [
'default' => null, 'default' => null,
'null' => true, 'null' => true,
]); ]);
$table->addColumn('foreign_key_uuid', 'uuid', [ $table->addColumn('foreign_key_uuid', 'uuid', [
'default' => null, 'default' => null,
'null' => true, 'null' => true,
]); ]);
$table->addColumn('model', 'string', [ $table->addColumn('model', 'string', [
'default' => null, 'default' => null,
'limit' => 255, 'limit' => 255,
'null' => false, 'null' => false,
]); ]);
$table->addColumn('cart_id', 'uuid', [ $table->addColumn('cart_id', 'uuid', [
'default' => null, 'default' => null,
'null' => false, 'null' => false,
]); ]);
$table->addColumn('position', 'integer', [ $table->addColumn('position', 'integer', [
'default' => null, 'default' => null,
'limit' => 11, 'limit' => 11,
'null' => true, 'null' => true,
]); ]);
$table->addColumn('qty', 'integer', [ $table->addColumn('qty', 'integer', [
'default' => null, 'default' => null,
'limit' => 11, 'limit' => 11,
'null' => false, 'null' => false,
]); ]);
$table->addColumn('price', 'decimal', [ $table->addColumn('price', 'decimal', [
'default' => null, 'default' => null,
'null' => true, 'null' => true,
'precision' => 13, 'precision' => 13,
'scale' => 5, 'scale' => 5,
]); ]);
$table->addColumn('subtotal', 'decimal', [ $table->addColumn('subtotal', 'decimal', [
'default' => null, 'default' => null,
'null' => true, 'null' => true,
'precision' => 13, 'precision' => 13,
'scale' => 5, 'scale' => 5,
]); ]);
$table->create();
}
$table->create();
}
} }

21
phpcs.xml Normal file
View File

@@ -0,0 +1,21 @@
<?xml version="1.0"?>
<ruleset name="plugin">
<arg value="nps"/>
<file>src/</file>
<file>config/</file>
<file>tests/</file>
<exclude-pattern>/tests/test_files/</exclude-pattern>
<exclude-pattern>/tests/test_app/</exclude-pattern>
<rule ref="vendor/fig-r/psr2r-sniffer/PSR2R/ruleset.xml"/>
<rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace">
<exclude-pattern>*/config/Migrations/*</exclude-pattern>
</rule>
<rule ref="PhpCollective.Classes.ClassFileName.NoMatch">
<exclude-pattern>*/config/Migrations/*</exclude-pattern>
</rule>
</ruleset>

10
phpstan.neon Normal file
View File

@@ -0,0 +1,10 @@
includes:
- vendor/cakedc/cakephp-phpstan/extension.neon
parameters:
level: 4
paths:
- src
bootstrapFiles:
- tests/bootstrap.php
treatPhpDocTypesAsCertain: false

View File

@@ -13,9 +13,9 @@ use Cake\Routing\RouteBuilder;
/** /**
* Plugin for CakeCarts * Plugin for CakeCarts
*/ */
class CakeCartsPlugin extends BasePlugin class CakeCartsPlugin extends BasePlugin {
{
/** /**
* Load all the plugin configuration and bootstrap logic. * Load all the plugin configuration and bootstrap logic.
* *
* The host application is provided as an argument. This allows you to load * The host application is provided as an argument. This allows you to load
@@ -24,12 +24,11 @@ class CakeCartsPlugin extends BasePlugin
* @param \Cake\Core\PluginApplicationInterface $app The host application * @param \Cake\Core\PluginApplicationInterface $app The host application
* @return void * @return void
*/ */
public function bootstrap(PluginApplicationInterface $app): void public function bootstrap(PluginApplicationInterface $app): void {
{ // remove this method hook if you don't need it
// remove this method hook if you don't need it }
}
/** /**
* Add routes for the plugin. * Add routes for the plugin.
* *
* If your plugin has many routes and you would like to isolate them into a separate file, * If your plugin has many routes and you would like to isolate them into a separate file,
@@ -38,62 +37,59 @@ class CakeCartsPlugin extends BasePlugin
* @param \Cake\Routing\RouteBuilder $routes The route builder to update. * @param \Cake\Routing\RouteBuilder $routes The route builder to update.
* @return void * @return void
*/ */
public function routes(RouteBuilder $routes): void public function routes(RouteBuilder $routes): void {
{ // remove this method hook if you don't need it
// remove this method hook if you don't need it $routes->plugin(
$routes->plugin( 'CakeCarts',
'CakeCarts', ['path' => '/cake-carts'],
['path' => '/cake-carts'], function (RouteBuilder $builder) {
function (RouteBuilder $builder) { // Add custom routes here
// Add custom routes here $builder->connect('/wishlist', ['controller' => 'CakeCarts', 'action' => 'wishlist']);
$builder->connect('/wishlist', ['controller' => 'CakeCarts', 'action' => 'wishlist']);
$builder->fallbacks(); $builder->fallbacks();
} },
); );
parent::routes($routes); parent::routes($routes);
} }
/** /**
* Add middleware for the plugin. * Add middleware for the plugin.
* *
* @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to update. * @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to update.
* @return \Cake\Http\MiddlewareQueue * @return \Cake\Http\MiddlewareQueue
*/ */
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue {
{ // Add your middlewares here
// Add your middlewares here // remove this method hook if you don't need it
// remove this method hook if you don't need it
return $middlewareQueue; return $middlewareQueue;
} }
/** /**
* Add commands for the plugin. * Add commands for the plugin.
* *
* @param \Cake\Console\CommandCollection $commands The command collection to update. * @param \Cake\Console\CommandCollection $commands The command collection to update.
* @return \Cake\Console\CommandCollection * @return \Cake\Console\CommandCollection
*/ */
public function console(CommandCollection $commands): CommandCollection public function console(CommandCollection $commands): CommandCollection {
{ // Add your commands here
// Add your commands here // remove this method hook if you don't need it
// remove this method hook if you don't need it
$commands = parent::console($commands); $commands = parent::console($commands);
return $commands; return $commands;
} }
/** /**
* Register application container services. * Register application container services.
* *
* @link https://book.cakephp.org/5/en/development/dependency-injection.html#dependency-injection
* @param \Cake\Core\ContainerInterface $container The Container to update. * @param \Cake\Core\ContainerInterface $container The Container to update.
* @return void * @return void
* @link https://book.cakephp.org/5/en/development/dependency-injection.html#dependency-injection
*/ */
public function services(ContainerInterface $container): void public function services(ContainerInterface $container): void {
{ // Add your services here
// Add your services here // remove this method hook if you don't need it
// remove this method hook if you don't need it }
}
} }

View File

@@ -5,6 +5,5 @@ namespace CakeCarts\Controller;
use App\Controller\AppController as BaseController; use App\Controller\AppController as BaseController;
class AppController extends BaseController class AppController extends BaseController {
{
} }

View File

@@ -4,10 +4,7 @@ declare(strict_types=1);
namespace CakeCarts\Controller; namespace CakeCarts\Controller;
use Cake\Core\Configure; use Cake\Core\Configure;
use Cake\Datasource\Exception\RecordNotFoundException;
use Cake\Http\Client;
use Cake\Log\Log; use Cake\Log\Log;
use CakeCarts\Controller\AppController;
/** /**
* CartItems Controller * CartItems Controller
@@ -15,148 +12,148 @@ use CakeCarts\Controller\AppController;
* @property \CakeCarts\Model\Table\CartItemsTable $CartItems * @property \CakeCarts\Model\Table\CartItemsTable $CartItems
* @property \Authorization\Controller\Component\AuthorizationComponent $Authorization * @property \Authorization\Controller\Component\AuthorizationComponent $Authorization
*/ */
class CartItemsController extends AppController class CartItemsController extends AppController {
{
public function initialize(): void
{
parent::initialize(); // TODO: Change the autogenerated stub
if (!$this->components()->has('ShoppingCart')) { /**
$this->loadComponent('CakeCarts.ShoppingCart', [ * @return void
// This is default config. You can modify "actions" as needed to make */
// component work only for specified methods. public function initialize(): void {
'actions' => ['add'], parent::initialize(); // TODO: Change the autogenerated stub
]);
}
if ($this->components()->has('Authorization')) { if (!$this->components()->has('ShoppingCart')) {
$this->Authorization->skipAuthorization(); $this->loadComponent('CakeCarts.ShoppingCart', [
} // This is default config. You can modify "actions" as needed to make
} // component work only for specified methods.
'actions' => ['add'],
]);
}
/** if ($this->components()->has('Authorization')) {
$this->Authorization->skipAuthorization();
}
}
/**
* Add method * Add method
* *
* @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise. * @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise.
*/ */
public function add() public function add() {
{ $this->request->allowMethod(['post', 'put', 'patch']);
$this->request->allowMethod(['post', 'put', 'patch']);
Log::debug(print_r('$this->request->getData()', true)); Log::debug(print_r('$this->request->getData()', true));
Log::debug(print_r($this->request->getData(), true)); Log::debug(print_r($this->request->getData(), true));
$cart = $this->viewBuilder()->getVar('cart'); $cart = $this->viewBuilder()->getVar('cart');
$postData = $this->request->getData(); $postData = $this->request->getData();
$postData['cart_id'] = $cart->id; $postData['cart_id'] = $cart->id;
// get product skus with variants // get product skus with variants
$price = $this->request->getData('price'); $price = $this->request->getData('price');
$qty = $this->request->getData('qty', 1); $qty = $this->request->getData('qty', 1);
$postData['price'] = $price; $postData['price'] = $price;
$postData['subtotal'] = isset($price) ? bcmul("$price", "$qty", 5) : null; $postData['subtotal'] = isset($price) ? bcmul("$price", "$qty", 5) : null;
$newCartItem = $this->CartItems->newEntity($postData, [ $newCartItem = $this->CartItems->newEntity($postData, [
'validate' => Configure::readOrFail('CakeCarts.CartItems.requirePricing') ? 'requirePricing' : 'default', 'validate' => Configure::readOrFail('CakeCarts.CartItems.requirePricing') ? 'requirePricing' : 'default',
]); ]);
if ($this->CartItems->save($newCartItem)) { if ($this->CartItems->save($newCartItem)) {
$this->Flash->success('Added to cart'); $this->Flash->success('Added to cart');
return $this->redirect($this->referer([ return $this->redirect($this->referer([
'plugin' => 'CakeCarts', 'plugin' => 'CakeCarts',
'controller' => 'CartItems', 'controller' => 'CartItems',
'action' => 'index' 'action' => 'index',
])); ]));
} }
// Log::debug(print_r('$newCartItem->getErrors()', true)); // Log::debug(print_r('$newCartItem->getErrors()', true));
// Log::debug(print_r($newCartItem->getErrors(), true)); // Log::debug(print_r($newCartItem->getErrors(), true));
$this->Flash->error('Failed to add to cart.'); $this->Flash->error('Failed to add to cart.');
return $this->redirect($this->referer([ return $this->redirect($this->referer([
'plugin' => 'CakeCarts', 'plugin' => 'CakeCarts',
'controller' => 'CartItems', 'controller' => 'CartItems',
'action' => 'index' 'action' => 'index',
])); ]));
} }
/** /**
* Edit method * Edit method
* *
* @param string|null $id Cart Item id. * @param string|null $id Cart Item id.
* @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
* @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
*/ */
public function edit($id = null) public function edit($id = null) {
{ $this->request->allowMethod(['post', 'put', 'patch']);
$this->request->allowMethod(['post', 'put', 'patch']);
$cartItem = $this->CartItems->find() $cartItem = $this->CartItems->find()
->where(['CartItems.id' => $id]) ->where(['CartItems.id' => $id])
->contain(['Carts']) ->contain(['Carts'])
->firstOrFail(); ->firstOrFail();
$this->ShoppingCart->checkIfIsOwnCart($cartItem->cart); $this->ShoppingCart->checkIfIsOwnCart($cartItem->cart);
if ($this->request->is(['patch', 'post', 'put'])) { if ($this->request->is(['patch', 'post', 'put'])) {
$postData = $this->request->getData(); $postData = $this->request->getData();
$qty = $this->request->getData('qty', 1); $qty = $this->request->getData('qty', 1);
$price = $this->request->getData('price', null); $price = $this->request->getData('price', null);
$subtotal = isset($price) ? bcmul("$qty", "$price", 5) : null; $subtotal = isset($price) ? bcmul("$qty", "$price", 5) : null;
$postData['subtotal'] = $subtotal; $postData['subtotal'] = $subtotal;
$cartItem = $this->CartItems->patchEntity($cartItem, $postData, [ $cartItem = $this->CartItems->patchEntity($cartItem, $postData, [
'validate' => Configure::readOrFail('CakeCarts.CartItems.requirePricing') ? 'requiresPricing' : 'default', 'validate' => Configure::readOrFail('CakeCarts.CartItems.requirePricing') ? 'requiresPricing' : 'default',
]); ]);
if ($this->CartItems->save($cartItem)) { if ($this->CartItems->save($cartItem)) {
$this->Flash->success(__('The cart item has been saved.')); $this->Flash->success(__('The cart item has been saved.'));
return $this->redirect($this->referer([ return $this->redirect($this->referer([
'plugin' => 'CakeCarts', 'plugin' => 'CakeCarts',
'controller' => 'CartItems', 'controller' => 'CartItems',
'action' => 'index' 'action' => 'index',
])); ]));
} }
Log::debug(print_r('$cartItem->getErrors()', true)); Log::debug(print_r('$cartItem->getErrors()', true));
Log::debug(print_r($cartItem->getErrors(), true)); Log::debug(print_r($cartItem->getErrors(), true));
$this->Flash->error(__('The cart item could not be saved. Please, try again.')); $this->Flash->error(__('The cart item could not be saved. Please, try again.'));
} }
return $this->redirect($this->referer([ return $this->redirect($this->referer([
'plugin' => 'CakeCarts', 'plugin' => 'CakeCarts',
'controller' => 'CartItems', 'controller' => 'CartItems',
'action' => 'index' 'action' => 'index',
])); ]));
} }
/** /**
* Delete method * Delete method
* *
* @param string|null $id Cart Item id. * @param string|null $id Cart Item id.
* @return \Cake\Http\Response|null Redirects to index.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
* @return \Cake\Http\Response|null Redirects to index.
*/ */
public function delete($id = null) public function delete($id = null) {
{ $this->request->allowMethod(['post', 'delete']);
$this->request->allowMethod(['post', 'delete']); $identity = $this->getRequest()->getAttribute('identity');
$identity = $this->getRequest()->getAttribute('identity');
// $cart = $this->viewBuilder()->getVar('cart'); // $cart = $this->viewBuilder()->getVar('cart');
$cartItem = $this->CartItems->find() $cartItem = $this->CartItems->find()
->where(['CartItems.id' => $id]) ->where(['CartItems.id' => $id])
->contain(['Carts']) ->contain(['Carts'])
->firstOrFail(); ->firstOrFail();
$this->ShoppingCart->checkIfIsOwnCart($cartItem->cart); $this->ShoppingCart->checkIfIsOwnCart($cartItem->cart);
unset($cartItem->cart); unset($cartItem->cart);
if ($this->CartItems->delete($cartItem)) { if ($this->CartItems->delete($cartItem)) {
$this->Flash->success(__('The cart item has been deleted.')); $this->Flash->success(__('The cart item has been deleted.'));
} else { } else {
$this->Flash->error(__('The cart item could not be deleted. Please, try again.')); $this->Flash->error(__('The cart item could not be deleted. Please, try again.'));
} }
return $this->redirect($this->referer([
'plugin' => 'CakeCarts',
'controller' => 'CartItems',
'action' => 'index',
]));
}
return $this->redirect($this->referer([
'plugin' => 'CakeCarts',
'controller' => 'CartItems',
'action' => 'index'
]));
}
} }

View File

@@ -4,37 +4,36 @@ declare(strict_types=1);
namespace CakeCarts\Controller; namespace CakeCarts\Controller;
use App\Controller\AppController; use App\Controller\AppController;
use Cake\Core\Configure;
use Cake\Event\EventInterface;
use CakeCarts\Model\Enum\CartTypeId;
/** /**
* Carts Controller * Carts Controller
* *
* @property \Authorization\Controller\Component\AuthorizationComponent $Authorization * @property \Authorization\Controller\Component\AuthorizationComponent $Authorization
*/ */
class CartsController extends AppController class CartsController extends AppController {
{
public function initialize(): void
{
parent::initialize(); // TODO: Change the autogenerated stub
if (!$this->components()->has('ShoppingCart')) { /**
$this->loadComponent('CakeCarts.ShoppingCart', [ * @return void
// This is default config. You can modify "actions" as needed to make */
// component work only for specified methods. public function initialize(): void {
'actions' => true, parent::initialize(); // TODO: Change the autogenerated stub
]);
}
}
/** if (!$this->components()->has('ShoppingCart')) {
$this->loadComponent('CakeCarts.ShoppingCart', [
// This is default config. You can modify "actions" as needed to make
// component work only for specified methods.
'actions' => true,
]);
}
}
/**
* Index method * Index method
* *
* @return \Cake\Http\Response|null|void Renders view * @return \Cake\Http\Response|null|void Renders view
*/ */
public function index() public function index() {
{ // use cart from beforeFilter
// use cart from beforeFilter }
}
} }

View File

@@ -10,159 +10,156 @@ use Cake\Datasource\Exception\RecordNotFoundException;
use Cake\Event\EventInterface; use Cake\Event\EventInterface;
use Cake\ORM\Table; use Cake\ORM\Table;
use Cake\ORM\TableRegistry; use Cake\ORM\TableRegistry;
use CakeCarts\Model\Entity\CartItem;
use CakeCarts\Model\Enum\CartTypeId; use CakeCarts\Model\Enum\CartTypeId;
use CakeCarts\Model\Table\CartsTable; use CakeCarts\Model\Table\CartsTable;
/** /**
* ShoppingCart component * ShoppingCart component
*/ */
class ShoppingCartComponent extends Component class ShoppingCartComponent extends Component {
{
/** /**
* @var string $userIdField * @var string $userIdField
*/ */
protected string $userIdField; protected string $userIdField;
/** /**
* @var CartsTable|Table $Carts * @var \CakeCarts\Model\Table\CartsTable|\Cake\ORM\Table $Carts
*/ */
protected CartsTable|Table $Carts; protected CartsTable|Table $Carts;
/**
* @return void
*/
public function initialize(array $config): void {
parent::initialize($config); // TODO: Change the autogenerated stub
public function initialize(array $config): void $this->userIdField = Configure::readOrFail('CakeCarts.Users.user_id') === 'uuid' ? 'user_id_uuid' : 'user_id';
{ $this->Carts = TableRegistry::getTableLocator()->get(Configure::readOrFail('CakeCarts.Carts.table'));
parent::initialize($config); // TODO: Change the autogenerated stub }
$this->userIdField = Configure::readOrFail('CakeCarts.Users.user_id') === 'uuid' ? 'user_id_uuid' : 'user_id'; /**
$this->Carts = TableRegistry::getTableLocator()->get(Configure::readOrFail('CakeCarts.Carts.table'));
}
/**
* Default configuration. * Default configuration.
* *
* @var array<string, mixed> * @var array<string, mixed>
*/ */
protected array $_defaultConfig = []; protected array $_defaultConfig = [];
public function beforeFilter(EventInterface $event): void /**
{ * @return void
if (!$this->_isActionEnabled()) { */
return; public function beforeFilter(EventInterface $event): void {
} if (!$this->_isActionEnabled()) {
return;
}
$sessionId = $this->getSessionId(); $sessionId = $this->getSessionId();
$cart = $this->findExistingCartOrCreate($sessionId); $cart = $this->findExistingCartOrCreate($sessionId);
$this->getController()->set(compact('cart')); $this->getController()->set(compact('cart'));
} }
/** /**
* @param string $cartId * @param string $cartId
* *
* @return mixed * @return mixed
*/ */
public function getCartForUserById(string $cartId) public function getCartForUserById(string $cartId) {
{ $identity = $this->getController()->getRequest()->getAttribute('identity');
$identity = $this->getController()->getRequest()->getAttribute('identity'); $sessionId = $this->getSessionId();
$sessionId = $this->getSessionId();
$cartsQ = $this->Carts $cartsQ = $this->Carts
->find() ->find()
->contain(['CartItems']) ->contain(['CartItems'])
->where(['Carts.id' => $cartId]); ->where(['Carts.id' => $cartId]);
if ($identity) { if ($identity) {
$cartsQ->where([$this->userIdField => $identity->getIdentifier()]); $cartsQ->where([$this->userIdField => $identity->getIdentifier()]);
} else { } else {
$cartsQ->where(['session_id' => $sessionId]); $cartsQ->where(['session_id' => $sessionId]);
} }
return $cartsQ->firstOrFail(); return $cartsQ->firstOrFail();
} }
public function findExistingCartOrCreate(string $sessionId, int $cartTypeId = null) public function findExistingCartOrCreate(string $sessionId, int|null $cartTypeId = null) {
{ $identity = $this->getController()->getRequest()->getAttribute('identity');
$identity = $this->getController()->getRequest()->getAttribute('identity');
$cartTypeId = $cartTypeId ?? CartTypeId::Cart->value; $cartTypeId = $cartTypeId ?? CartTypeId::Cart->value;
$cart = $this->Carts $cart = $this->Carts
->findBySessionId($sessionId) ->findBySessionId($sessionId)
->contain(['CartItems']) ->contain(['CartItems'])
->where(['cart_type_id' => $cartTypeId]) ->where(['cart_type_id' => $cartTypeId])
->first(); ->first();
if (isset($cart) && isset($identity) && !isset($cart[$this->userIdField])) { if (isset($cart) && isset($identity) && !isset($cart[$this->userIdField])) {
$cart = $this->Carts->patchEntity($cart, [ $cart = $this->Carts->patchEntity($cart, [
$this->userIdField => $identity->getIdentifier(), $this->userIdField => $identity->getIdentifier(),
]); ]);
$cart = $this->Carts->saveOrFail($cart); $cart = $this->Carts->saveOrFail($cart);
} }
if (!isset($cart)) { if (!isset($cart)) {
$cart = $this->Carts->newEntity([ $cart = $this->Carts->newEntity([
'cart_type_id' => $cartTypeId, 'cart_type_id' => $cartTypeId,
'session_id' => $sessionId, 'session_id' => $sessionId,
$this->userIdField => isset($identity) ? $identity->getIdentifier() : null, $this->userIdField => isset($identity) ? $identity->getIdentifier() : null,
'num_items' => 0, 'num_items' => 0,
'cart_items' => [], 'cart_items' => [],
]); ]);
$cart = $this->Carts->saveOrFail($cart); $cart = $this->Carts->saveOrFail($cart);
} }
return $cart; return $cart;
} }
public function getUserIdField() public function getUserIdField() {
{ return $this->userIdField;
return $this->userIdField; }
}
/** /**
* @return string * @return string
*/ */
public function getSessionId(): string public function getSessionId(): string {
{ if (!$this->getController()->getRequest()->getSession()->started()) {
if (!$this->getController()->getRequest()->getSession()->started()) { $this->getController()->getRequest()->getSession()->start();
$this->getController()->getRequest()->getSession()->start(); }
}
if (!$this->getController()->getRequest()->getSession()->check('CakeCarts.session_id')) { if (!$this->getController()->getRequest()->getSession()->check('CakeCarts.session_id')) {
$this->getController()->getRequest()->getSession()->write('CakeCarts.session_id', $this->getController()->getRequest()->getSession()->id()); $this->getController()->getRequest()->getSession()->write('CakeCarts.session_id', $this->getController()->getRequest()->getSession()->id());
} }
return $this->getController()->getRequest()->getSession()->read('CakeCarts.session_id'); return $this->getController()->getRequest()->getSession()->read('CakeCarts.session_id');
} }
/** /**
* @param EntityInterface $cart * @param \Cake\Datasource\EntityInterface $cart
* @throws RecordNotFoundException * @throws \Cake\Datasource\Exception\RecordNotFoundException
* *
* @return void * @return void
*/ */
public function checkIfIsOwnCart(EntityInterface $cart): void public function checkIfIsOwnCart(EntityInterface $cart): void {
{ $identity = $this->getController()->getRequest()->getAttribute('identity');
$identity = $this->getController()->getRequest()->getAttribute('identity');
if (!isset($identity) && isset($cart->session_id) && ($cart->session_id != $this->getSessionId())) { if (!isset($identity) && isset($cart->session_id) && ($cart->session_id != $this->getSessionId())) {
throw new RecordNotFoundException(); throw new RecordNotFoundException();
} }
if (isset($identity) && $identity->getIdentifier() != $cart->get($this->getUserIdField())) { if (isset($identity) && $identity->getIdentifier() != $cart->get($this->getUserIdField())) {
throw new RecordNotFoundException(); throw new RecordNotFoundException();
} }
} }
/** /**
* @return bool * @return bool
*/ */
protected function _isActionEnabled(): bool protected function _isActionEnabled(): bool {
{ $actions = $this->getConfig('actions');
$actions = $this->getConfig('actions'); if (is_bool($actions)) {
if (is_bool($actions)) { return $actions;
return $actions; }
}
return in_array($this->getController()->getRequest()->getParam('action'), (array)$actions, true);
}
return in_array($this->getController()->getRequest()->getParam('action'), (array)$actions, true);
}
} }

View File

@@ -11,7 +11,7 @@ use Cake\ORM\Entity;
* @property string $id * @property string $id
* @property int $cart_type_id * @property int $cart_type_id
* @property string|null $session_id * @property string|null $session_id
* @property integer|null $user_id * @property int|null $user_id
* @property string|null $user_id_uuid * @property string|null $user_id_uuid
* @property \Cake\I18n\DateTime $created * @property \Cake\I18n\DateTime $created
* @property \Cake\I18n\DateTime|null $modified * @property \Cake\I18n\DateTime|null $modified
@@ -22,9 +22,9 @@ use Cake\ORM\Entity;
* *
* @property \CakeCarts\Model\Entity\CartItem[] $cart_items * @property \CakeCarts\Model\Entity\CartItem[] $cart_items
*/ */
class Cart extends Entity class Cart extends Entity {
{
/** /**
* Fields that can be mass assigned using newEntity() or patchEntity(). * Fields that can be mass assigned using newEntity() or patchEntity().
* *
* Note that when '*' is set to true, this allows all unspecified fields to * Note that when '*' is set to true, this allows all unspecified fields to
@@ -33,18 +33,19 @@ class Cart extends Entity
* *
* @var array<string, bool> * @var array<string, bool>
*/ */
protected array $_accessible = [ protected array $_accessible = [
'cart_type_id' => true, 'cart_type_id' => true,
'session_id' => true, 'session_id' => true,
'user_id' => true, 'user_id' => true,
'user_id_uuid' => true, 'user_id_uuid' => true,
'created' => true, 'created' => true,
'modified' => true, 'modified' => true,
'deleted' => true, 'deleted' => true,
'removed' => true, 'removed' => true,
'removed_reason_id' => true, 'removed_reason_id' => true,
'num_items' => true, 'num_items' => true,
'user' => true, 'user' => true,
'cart_items' => true, 'cart_items' => true,
]; ];
} }

View File

@@ -20,9 +20,9 @@ use Cake\ORM\Entity;
* *
* @property \CakeCarts\Model\Entity\Cart $cart * @property \CakeCarts\Model\Entity\Cart $cart
*/ */
class CartItem extends Entity class CartItem extends Entity {
{
/** /**
* Fields that can be mass assigned using newEntity() or patchEntity(). * Fields that can be mass assigned using newEntity() or patchEntity().
* *
* Note that when '*' is set to true, this allows all unspecified fields to * Note that when '*' is set to true, this allows all unspecified fields to
@@ -31,15 +31,16 @@ class CartItem extends Entity
* *
* @var array<string, bool> * @var array<string, bool>
*/ */
protected array $_accessible = [ protected array $_accessible = [
'foreign_key' => true, 'foreign_key' => true,
'foreign_key_uuid' => true, 'foreign_key_uuid' => true,
'model' => true, 'model' => true,
'cart_id' => true, 'cart_id' => true,
'position' => true, 'position' => true,
'qty' => true, 'qty' => true,
'price' => true, 'price' => true,
'subtotal' => true, 'subtotal' => true,
'cart' => true, 'cart' => true,
]; ];
} }

View File

@@ -1,4 +1,5 @@
<?php <?php
namespace CakeCarts\Model\Enum; namespace CakeCarts\Model\Enum;
use Cake\Database\Type\EnumLabelInterface; use Cake\Database\Type\EnumLabelInterface;
@@ -6,18 +7,17 @@ use Tools\Model\Enum\EnumOptionsTrait;
enum CartTypeId: int implements EnumLabelInterface enum CartTypeId: int implements EnumLabelInterface
{ {
use EnumOptionsTrait; use EnumOptionsTrait;
case Cart = 1; case Cart = 1;
case Wishlist = 2; case Wishlist = 2;
case CustomList = 3; case CustomList = 3;
public function label(): string public function label(): string {
{ return match ($this) {
return match($this) { static::Cart => 'Cart',
self::Cart => 'Cart', static::Wishlist => 'Wishlist',
self::Wishlist => 'Wishlist', static::CustomList => 'List'
self::CustomList => 'List' };
}; }
}
} }

View File

@@ -3,7 +3,6 @@ declare(strict_types=1);
namespace CakeCarts\Model\Table; namespace CakeCarts\Model\Table;
use Cake\ORM\Query\SelectQuery;
use Cake\ORM\RulesChecker; use Cake\ORM\RulesChecker;
use Cake\ORM\Table; use Cake\ORM\Table;
use Cake\Validation\Validator; use Cake\Validation\Validator;
@@ -27,94 +26,91 @@ use Cake\Validation\Validator;
* @method iterable<\CakeCarts\Model\Entity\CartItem>|\Cake\Datasource\ResultSetInterface<\CakeCarts\Model\Entity\CartItem>|false deleteMany(iterable $entities, array $options = []) * @method iterable<\CakeCarts\Model\Entity\CartItem>|\Cake\Datasource\ResultSetInterface<\CakeCarts\Model\Entity\CartItem>|false deleteMany(iterable $entities, array $options = [])
* @method iterable<\CakeCarts\Model\Entity\CartItem>|\Cake\Datasource\ResultSetInterface<\CakeCarts\Model\Entity\CartItem> deleteManyOrFail(iterable $entities, array $options = []) * @method iterable<\CakeCarts\Model\Entity\CartItem>|\Cake\Datasource\ResultSetInterface<\CakeCarts\Model\Entity\CartItem> deleteManyOrFail(iterable $entities, array $options = [])
*/ */
class CartItemsTable extends Table class CartItemsTable extends Table {
{
/** /**
* Initialize method * Initialize method
* *
* @param array<string, mixed> $config The configuration for the Table. * @param array<string, mixed> $config The configuration for the Table.
* @return void * @return void
*/ */
public function initialize(array $config): void public function initialize(array $config): void {
{ parent::initialize($config);
parent::initialize($config);
$this->setTable('cart_items'); $this->setTable('cart_items');
$this->setDisplayField('id'); $this->setDisplayField('id');
$this->setPrimaryKey('id'); $this->setPrimaryKey('id');
$this->belongsTo('Carts', [ $this->belongsTo('Carts', [
'foreignKey' => 'cart_id', 'foreignKey' => 'cart_id',
'joinType' => 'INNER', 'joinType' => 'INNER',
'className' => 'CakeCarts.Carts', 'className' => 'CakeCarts.Carts',
]); ]);
} }
/** /**
* Default validation rules. * Default validation rules.
* *
* @param \Cake\Validation\Validator $validator Validator instance. * @param \Cake\Validation\Validator $validator Validator instance.
* @return \Cake\Validation\Validator * @return \Cake\Validation\Validator
*/ */
public function validationDefault(Validator $validator): Validator public function validationDefault(Validator $validator): Validator {
{ $validator
$validator ->scalar('foreign_key')
->scalar('foreign_key') ->allowEmptyString('foreign_key');
->allowEmptyString('foreign_key');
$validator $validator
->uuid('foreign_key_uuid') ->uuid('foreign_key_uuid')
->allowEmptyString('foreign_key_uuid'); ->allowEmptyString('foreign_key_uuid');
$validator $validator
->scalar('model') ->scalar('model')
->requirePresence('model', 'create') ->requirePresence('model', 'create')
->notEmptyString('model'); ->notEmptyString('model');
$validator $validator
->uuid('cart_id') ->uuid('cart_id')
->notEmptyString('cart_id'); ->notEmptyString('cart_id');
$validator $validator
->integer('position') ->integer('position')
->allowEmptyString('position'); ->allowEmptyString('position');
$validator $validator
->integer('qty') ->integer('qty')
->requirePresence('qty', 'create') ->requirePresence('qty', 'create')
->notEmptyString('qty'); ->notEmptyString('qty');
return $validator; return $validator;
} }
public function validationRequiresPricing(Validator $validator): Validator public function validationRequiresPricing(Validator $validator): Validator {
{ $validator = $this->validationDefault($validator);
$validator = $this->validationDefault($validator);
$validator $validator
->decimal('price') ->decimal('price')
->requirePresence('price', 'create') ->requirePresence('price', 'create')
->allowEmptyString('price'); ->allowEmptyString('price');
$validator $validator
->decimal('subtotal') ->decimal('subtotal')
->requirePresence('subtotal', 'create') ->requirePresence('subtotal', 'create')
->notEmptyString('subtotal'); ->notEmptyString('subtotal');
return $validator; return $validator;
} }
/** /**
* Returns a rules checker object that will be used for validating * Returns a rules checker object that will be used for validating
* application integrity. * application integrity.
* *
* @param \Cake\ORM\RulesChecker $rules The rules object to be modified. * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
* @return \Cake\ORM\RulesChecker * @return \Cake\ORM\RulesChecker
*/ */
public function buildRules(RulesChecker $rules): RulesChecker public function buildRules(RulesChecker $rules): RulesChecker {
{ $rules->add($rules->existsIn(['cart_id'], 'Carts'), ['errorField' => 'cart_id']);
$rules->add($rules->existsIn(['cart_id'], 'Carts'), ['errorField' => 'cart_id']);
return $rules;
}
return $rules;
}
} }

View File

@@ -4,8 +4,6 @@ declare(strict_types=1);
namespace CakeCarts\Model\Table; namespace CakeCarts\Model\Table;
use Cake\Database\Type\EnumType; use Cake\Database\Type\EnumType;
use Cake\ORM\Query\SelectQuery;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table; use Cake\ORM\Table;
use Cake\Validation\Validator; use Cake\Validation\Validator;
use CakeCarts\Model\Enum\CartTypeId; use CakeCarts\Model\Enum\CartTypeId;
@@ -32,74 +30,73 @@ use CakeCarts\Model\Enum\CartTypeId;
* *
* @mixin \Cake\ORM\Behavior\TimestampBehavior * @mixin \Cake\ORM\Behavior\TimestampBehavior
*/ */
class CartsTable extends Table class CartsTable extends Table {
{
/** /**
* Initialize method * Initialize method
* *
* @param array<string, mixed> $config The configuration for the Table. * @param array<string, mixed> $config The configuration for the Table.
* @return void * @return void
*/ */
public function initialize(array $config): void public function initialize(array $config): void {
{ parent::initialize($config);
parent::initialize($config);
$this->setTable('carts'); $this->setTable('carts');
$this->setDisplayField('id'); $this->setDisplayField('id');
$this->setPrimaryKey('id'); $this->setPrimaryKey('id');
$this->addBehavior('Timestamp'); $this->addBehavior('Timestamp');
$this->hasMany('CartItems', [ $this->hasMany('CartItems', [
'foreignKey' => 'cart_id', 'foreignKey' => 'cart_id',
'className' => 'CakeCarts.CartItems', 'className' => 'CakeCarts.CartItems',
]); ]);
$this->getSchema()->setColumnType('cart_type_id', EnumType::from(CartTypeId::class)); $this->getSchema()->setColumnType('cart_type_id', EnumType::from(CartTypeId::class));
} }
/** /**
* Default validation rules. * Default validation rules.
* *
* @param \Cake\Validation\Validator $validator Validator instance. * @param \Cake\Validation\Validator $validator Validator instance.
* @return \Cake\Validation\Validator * @return \Cake\Validation\Validator
*/ */
public function validationDefault(Validator $validator): Validator public function validationDefault(Validator $validator): Validator {
{ $validator
$validator ->integer('cart_type_id')
->integer('cart_type_id') ->requirePresence('cart_type_id', 'create')
->requirePresence('cart_type_id', 'create') ->notEmptyString('cart_type_id');
->notEmptyString('cart_type_id');
$validator $validator
->scalar('session_id') ->scalar('session_id')
->maxLength('session_id', 255) ->maxLength('session_id', 255)
->allowEmptyString('session_id'); ->allowEmptyString('session_id');
$validator $validator
->uuid('user_id_uuid') ->uuid('user_id_uuid')
->allowEmptyString('user_id_uuid'); ->allowEmptyString('user_id_uuid');
$validator $validator
->integer('user_id') ->integer('user_id')
->allowEmptyString('user_id'); ->allowEmptyString('user_id');
$validator $validator
->dateTime('deleted') ->dateTime('deleted')
->allowEmptyDateTime('deleted'); ->allowEmptyDateTime('deleted');
$validator $validator
->dateTime('removed') ->dateTime('removed')
->allowEmptyDateTime('removed'); ->allowEmptyDateTime('removed');
$validator $validator
->integer('removed_reason_id') ->integer('removed_reason_id')
->allowEmptyString('removed_reason_id'); ->allowEmptyString('removed_reason_id');
$validator $validator
->integer('num_items') ->integer('num_items')
->notEmptyString('num_items'); ->notEmptyString('num_items');
return $validator;
}
return $validator;
}
} }

View File

@@ -8,61 +8,61 @@ use Cake\TestSuite\Fixture\TestFixture;
/** /**
* CartItemsFixture * CartItemsFixture
*/ */
class CartItemsFixture extends TestFixture class CartItemsFixture extends TestFixture {
{
/** /**
* Init method * Init method
* *
* @return void * @return void
*/ */
public function init(): void public function init(): void {
{ $this->records = [
$this->records = [ [
[ 'id' => '79f66e8d-8d8d-4095-adc4-fd15234a4397',
'id' => '79f66e8d-8d8d-4095-adc4-fd15234a4397', 'foreign_key' => null,
'foreign_key' => null, 'foreign_key_uuid' => 'e5efe749-d6b6-4f72-83c9-32b19936c70c',
'foreign_key_uuid' => 'e5efe749-d6b6-4f72-83c9-32b19936c70c', 'model' => 'ProductSkus',
'model' => 'ProductSkus', 'cart_id' => '74d1aa54-92a2-4039-bc10-61e1190c51ea',
'cart_id' => '74d1aa54-92a2-4039-bc10-61e1190c51ea', 'position' => 1,
'position' => 1, 'qty' => 1,
'qty' => 1, 'price' => 1.5,
'price' => 1.5, 'subtotal' => 1.5,
'subtotal' => 1.5, ],
], [
[ 'id' => '74d1aa54-92a2-4039-ba10-61e1190c51eb',
'id' => '74d1aa54-92a2-4039-ba10-61e1190c51eb', 'foreign_key' => null,
'foreign_key' => null, 'foreign_key_uuid' => 'e5efe749-d6b6-4f72-83c9-32b19936c70c',
'foreign_key_uuid' => 'e5efe749-d6b6-4f72-83c9-32b19936c70c', 'model' => 'ProductSkus',
'model' => 'ProductSkus', 'cart_id' => '74d1aa54-92a2-4039-bc10-61e1190c51eb',
'cart_id' => '74d1aa54-92a2-4039-bc10-61e1190c51eb', 'position' => 1,
'position' => 1, 'qty' => 1,
'qty' => 1, 'price' => 1.5,
'price' => 1.5, 'subtotal' => 1.5,
'subtotal' => 1.5, ],
], [
[ 'id' => '74d1aa54-92a2-4039-bc10-61e4190c51ec',
'id' => '74d1aa54-92a2-4039-bc10-61e4190c51ec', 'foreign_key' => null,
'foreign_key' => null, 'foreign_key_uuid' => 'e5efe749-d6b6-4f72-83c9-32b19936c70c',
'foreign_key_uuid' => 'e5efe749-d6b6-4f72-83c9-32b19936c70c', 'model' => 'ProductSkus',
'model' => 'ProductSkus', 'cart_id' => '74d1aa54-92a2-4039-bc10-61e1190c51ec',
'cart_id' => '74d1aa54-92a2-4039-bc10-61e1190c51ec', 'position' => 1,
'position' => 1, 'qty' => 1,
'qty' => 1, 'price' => 1.5,
'price' => 1.5, 'subtotal' => 1.5,
'subtotal' => 1.5, ],
], [
[ 'id' => '79f66e8d-8d8d-4095-adc4-fd15234a4394',
'id' => '79f66e8d-8d8d-4095-adc4-fd15234a4394', 'foreign_key' => null,
'foreign_key' => null, 'foreign_key_uuid' => 'e5efe749-d6b6-4f72-83c9-32b19936c70c',
'foreign_key_uuid' => 'e5efe749-d6b6-4f72-83c9-32b19936c70c', 'model' => 'ProductSkus',
'model' => 'ProductSkus', 'cart_id' => '74d1aa54-92a2-4039-bc10-61e1190c51ed',
'cart_id' => '74d1aa54-92a2-4039-bc10-61e1190c51ed', 'position' => 1,
'position' => 1, 'qty' => 1,
'qty' => 1, 'price' => 1.5,
'price' => 1.5, 'subtotal' => 1.5,
'subtotal' => 1.5, ],
], ];
]; parent::init();
parent::init(); }
}
} }

View File

@@ -9,73 +9,73 @@ use CakeCarts\Model\Enum\CartTypeId;
/** /**
* CartsFixture * CartsFixture
*/ */
class CartsFixture extends TestFixture class CartsFixture extends TestFixture {
{
/** /**
* Init method * Init method
* *
* @return void * @return void
*/ */
public function init(): void public function init(): void {
{ $this->records = [
$this->records = [ // normal cart - open
// normal cart - open [
[ 'id' => '74d1aa54-92a2-4039-bc10-61e1190c51ea',
'id' => '74d1aa54-92a2-4039-bc10-61e1190c51ea', 'cart_type_id' => CartTypeId::Cart->value,
'cart_type_id' => CartTypeId::Cart->value, 'session_id' => 'session_1',
'session_id' => 'session_1', 'user_id' => null,
'user_id' => null, 'user_id_uuid' => '5a34a6ae-7d3f-4dcf-bac7-57335b51e697',
'user_id_uuid' => '5a34a6ae-7d3f-4dcf-bac7-57335b51e697', 'created' => '2025-10-08 09:55:15',
'created' => '2025-10-08 09:55:15', 'modified' => '2025-10-08 09:55:15',
'modified' => '2025-10-08 09:55:15', 'deleted' => null,
'deleted' => null, 'removed' => null,
'removed' => null, 'removed_reason_id' => null,
'removed_reason_id' => null, 'num_items' => 1,
'num_items' => 1, ],
], // normal cart - deleted
// normal cart - deleted [
[ 'id' => '74d1aa54-92a2-4039-bc10-61e1190c51eb',
'id' => '74d1aa54-92a2-4039-bc10-61e1190c51eb', 'cart_type_id' => CartTypeId::Cart->value,
'cart_type_id' => CartTypeId::Cart->value, 'session_id' => 'session_1',
'session_id' => 'session_1', 'user_id' => null,
'user_id' => null, 'user_id_uuid' => '5a34a6ae-7d3f-4dcf-bac7-57335b51e697',
'user_id_uuid' => '5a34a6ae-7d3f-4dcf-bac7-57335b51e697', 'created' => '2025-10-08 09:55:15',
'created' => '2025-10-08 09:55:15', 'modified' => '2025-10-08 09:55:15',
'modified' => '2025-10-08 09:55:15', 'deleted' => '2025-10-08 09:55:15',
'deleted' => '2025-10-08 09:55:15', 'removed' => null,
'removed' => null, 'removed_reason_id' => null,
'removed_reason_id' => null, 'num_items' => 1,
'num_items' => 1, ],
], // wishlist cart - open
// wishlist cart - open [
[ 'id' => '74d1aa54-92a2-4039-bc10-61e1190c51ec',
'id' => '74d1aa54-92a2-4039-bc10-61e1190c51ec', 'cart_type_id' => CartTypeId::Wishlist->value,
'cart_type_id' => CartTypeId::Wishlist->value, 'session_id' => 'session_2',
'session_id' => 'session_2', 'user_id' => null,
'user_id' => null, 'user_id_uuid' => '5a34a6ae-7d3f-4dcf-bac7-57335b51e697',
'user_id_uuid' => '5a34a6ae-7d3f-4dcf-bac7-57335b51e697', 'created' => '2025-10-08 09:55:15',
'created' => '2025-10-08 09:55:15', 'modified' => '2025-10-08 09:55:15',
'modified' => '2025-10-08 09:55:15', 'deleted' => null,
'deleted' => null, 'removed' => null,
'removed' => null, 'removed_reason_id' => null,
'removed_reason_id' => null, 'num_items' => 1,
'num_items' => 1, ],
], // wishlist cart - deleted
// wishlist cart - deleted [
[ 'id' => '74d1aa54-92a2-4039-bc10-61e1190c51ed',
'id' => '74d1aa54-92a2-4039-bc10-61e1190c51ed', 'cart_type_id' => CartTypeId::Wishlist->value,
'cart_type_id' => CartTypeId::Wishlist->value, 'session_id' => 'session_2',
'session_id' => 'session_2', 'user_id' => null,
'user_id' => null, 'user_id_uuid' => '5a34a6ae-7d3f-4dcf-bac7-57335b51e697',
'user_id_uuid' => '5a34a6ae-7d3f-4dcf-bac7-57335b51e697', 'created' => '2025-10-08 09:55:15',
'created' => '2025-10-08 09:55:15', 'modified' => '2025-10-08 09:55:15',
'modified' => '2025-10-08 09:55:15', 'deleted' => '2025-10-08 09:55:15',
'deleted' => '2025-10-08 09:55:15', 'removed' => null,
'removed' => null, 'removed_reason_id' => null,
'removed_reason_id' => null, 'num_items' => 1,
'num_items' => 1, ],
], ];
]; parent::init();
parent::init(); }
}
} }

View File

@@ -5,118 +5,115 @@ namespace CakeCarts\Test\TestCase\Controller;
use Cake\TestSuite\IntegrationTestTrait; use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase; use Cake\TestSuite\TestCase;
use CakeCarts\Controller\CartItemsController;
use CakeCarts\Model\Table\CartItemsTable; use CakeCarts\Model\Table\CartItemsTable;
use CakeCarts\Model\Table\CartsTable;
/** /**
* CakeCarts\Controller\CartItemsController Test Case * CakeCarts\Controller\CartItemsController Test Case
* *
* @link \CakeCarts\Controller\CartItemsController * @link \CakeCarts\Controller\CartItemsController
*/ */
class CartItemsControllerTest extends TestCase class CartItemsControllerTest extends TestCase {
{
use IntegrationTestTrait;
/** use IntegrationTestTrait;
/**
* Test subject * Test subject
* *
* @var \CakeCarts\Model\Table\CartItemsTable * @var \CakeCarts\Model\Table\CartItemsTable
*/ */
protected $CartItems; protected $CartItems;
/** /**
* Fixtures * Fixtures
* *
* @var array<string> * @var array<string>
*/ */
protected array $fixtures = [ protected array $fixtures = [
'plugin.CakeCarts.CartItems', 'plugin.CakeCarts.CartItems',
'plugin.CakeCarts.Carts', 'plugin.CakeCarts.Carts',
]; ];
public function setUp(): void /**
{ * @return void
parent::setUp(); // TODO: Change the autogenerated stub */
public function setUp(): void {
parent::setUp(); // TODO: Change the autogenerated stub
$config = $this->getTableLocator()->exists('CartItems') ? [] : ['className' => CartItemsTable::class]; $config = $this->getTableLocator()->exists('CartItems') ? [] : ['className' => CartItemsTable::class];
$this->CartItems = $this->getTableLocator()->get('CartItems', $config); $this->CartItems = $this->getTableLocator()->get('CartItems', $config);
} }
/**
/**
* Test add method * Test add method
* *
* @return void
* @link \CakeCarts\Controller\CartItemsController::add() * @link \CakeCarts\Controller\CartItemsController::add()
* @return void
*/ */
public function testAdd(): void public function testAdd(): void {
{ $url = [
$url = [ 'plugin' => 'CakeCarts',
'plugin' => 'CakeCarts', 'controller' => 'CartItems',
'controller' => 'CartItems', 'action' => 'add',
'action' => 'add', ];
]; $skuId = '3a477e3e-7977-4813-81f6-f85949613979';
$skuId = '3a477e3e-7977-4813-81f6-f85949613979';
$beforeCnt = $this->CartItems->find()->where(['foreign_key_uuid' => $skuId])->count(); $beforeCnt = $this->CartItems->find()->where(['foreign_key_uuid' => $skuId])->count();
$postData = [ $postData = [
'foreign_key' => null, 'foreign_key' => null,
'foreign_key_uuid' => $skuId, 'foreign_key_uuid' => $skuId,
'model' => 'ProductSkus', 'model' => 'ProductSkus',
'qty' => 10, 'qty' => 10,
'price' => 0.75, 'price' => 0.75,
'subtotal' => 1, 'subtotal' => 1,
]; ];
$this->post($url, $postData); $this->post($url, $postData);
$this->assertResponseCode(302); $this->assertResponseCode(302);
$afterCnt = $this->CartItems->find()->where(['foreign_key_uuid' => $skuId])->count(); $afterCnt = $this->CartItems->find()->where(['foreign_key_uuid' => $skuId])->count();
$this->assertEquals($beforeCnt + 1, $afterCnt); $this->assertEquals($beforeCnt + 1, $afterCnt);
$new = $this->CartItems->find()->where(['foreign_key_uuid' => $skuId, 'qty' => 10, 'model' => 'ProductSkus'])->firstOrFail(); $new = $this->CartItems->find()->where(['foreign_key_uuid' => $skuId, 'qty' => 10, 'model' => 'ProductSkus'])->firstOrFail();
$this->assertEquals(7.5, $new->subtotal); $this->assertEquals(7.5, $new->subtotal);
} }
/** /**
* Test edit method * Test edit method
* *
* @return void
* @link \CakeCarts\Controller\CartItemsController::edit() * @link \CakeCarts\Controller\CartItemsController::edit()
* @return void
*/ */
public function testEdit(): void public function testEdit(): void {
{ $id = '79f66e8d-8d8d-4095-adc4-fd15234a4397';
$id = '79f66e8d-8d8d-4095-adc4-fd15234a4397'; $url = [
$url = [ 'plugin' => 'CakeCarts',
'plugin' => 'CakeCarts', 'controller' => 'CartItems',
'controller' => 'CartItems', 'action' => 'edit',
'action' => 'edit', $id,
$id ];
]; $this->session(['Auth.User.id' => 1]);
$this->session(['Auth.User.id' => 1]); $this->session(['Auth.id' => 1]);
$this->session(['Auth.id' => 1]); $this->session(['CakeCarts.session_id' => 'session_1']);
$this->session(['CakeCarts.session_id' => 'session_1']); $before = $this->CartItems->get($id, contain: ['Carts']);
$before = $this->CartItems->get($id, contain: ['Carts']);
// dd($before); // dd($before);
$skuId = '3a477e3e-7977-4813-81f6-f85949613979'; $skuId = '3a477e3e-7977-4813-81f6-f85949613979';
$postData = [ $postData = [
'qty' => 100, 'qty' => 100,
]; ];
$this->post($url, $postData); $this->post($url, $postData);
$this->assertResponseCode(302); $this->assertResponseCode(302);
$new = $this->CartItems->get($id); $new = $this->CartItems->get($id);
$this->assertEquals(100, $new->qty); $this->assertEquals(100, $new->qty);
} }
/** /**
* Test delete method * Test delete method
* *
* @return void
* @link \CakeCarts\Controller\CartItemsController::delete() * @link \CakeCarts\Controller\CartItemsController::delete()
* @return void
*/ */
public function testDelete(): void public function testDelete(): void {
{ $this->markTestIncomplete('Not implemented yet.');
$this->markTestIncomplete('Not implemented yet.'); }
}
} }

View File

@@ -5,41 +5,45 @@ namespace CakeCarts\Test\TestCase\Controller;
use Cake\TestSuite\IntegrationTestTrait; use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase; use Cake\TestSuite\TestCase;
use CakeCarts\Controller\CartsController;
/** /**
* CakeCarts\Controller\CartsController Test Case * CakeCarts\Controller\CartsController Test Case
* *
* @uses \CakeCarts\Controller\CartsController * @uses \CakeCarts\Controller\CartsController
*/ */
class CartsControllerTest extends TestCase class CartsControllerTest extends TestCase {
{
use IntegrationTestTrait;
/** use IntegrationTestTrait;
/**
* Test subject * Test subject
* *
* @var \CakeCarts\Model\Table\CartItemsTable * @var \CakeCarts\Model\Table\CartItemsTable
*/ */
protected $Carts; protected $Carts;
public function setUp(): void /**
{ * @return void
parent::setUp(); // TODO: Change the autogenerated stub */
public function setUp(): void {
parent::setUp(); // TODO: Change the autogenerated stub
$config = $this->getTableLocator()->exists('Carts') ? [] : ['className' => CartsTable::class]; $config = $this->getTableLocator()->exists('Carts') ? [] : ['className' => CartsTable::class];
$this->Carts = $this->getTableLocator()->get('Carts', $config); $this->Carts = $this->getTableLocator()->get('Carts', $config);
} }
public function testIndex(): void /**
{ * @return void
$url = [ */
'plugin' => 'CakeCarts', public function testIndex(): void {
'controller' => 'Carts', $url = [
'action' => 'index', 'plugin' => 'CakeCarts',
]; 'controller' => 'Carts',
$this->get($url); 'action' => 'index',
];
$this->get($url);
$this->assertResponseCode(200);
}
$this->assertResponseCode(200);
}
} }

View File

@@ -10,36 +10,35 @@ use CakeCarts\Controller\Component\ShoppingCartComponent;
/** /**
* CakeCarts\Controller\Component\ShoppingCartComponent Test Case * CakeCarts\Controller\Component\ShoppingCartComponent Test Case
*/ */
class ShoppingCartComponentTest extends TestCase class ShoppingCartComponentTest extends TestCase {
{
/** /**
* Test subject * Test subject
* *
* @var \CakeCarts\Controller\Component\ShoppingCartComponent * @var \CakeCarts\Controller\Component\ShoppingCartComponent
*/ */
protected $ShoppingCart; protected $ShoppingCart;
/** /**
* setUp method * setUp method
* *
* @return void * @return void
*/ */
protected function setUp(): void protected function setUp(): void {
{ parent::setUp();
parent::setUp(); $registry = new ComponentRegistry();
$registry = new ComponentRegistry(); $this->ShoppingCart = new ShoppingCartComponent($registry);
$this->ShoppingCart = new ShoppingCartComponent($registry); }
}
/** /**
* tearDown method * tearDown method
* *
* @return void * @return void
*/ */
protected function tearDown(): void protected function tearDown(): void {
{ unset($this->ShoppingCart);
unset($this->ShoppingCart);
parent::tearDown();
}
parent::tearDown();
}
} }

View File

@@ -9,68 +9,65 @@ use CakeCarts\Model\Table\CartItemsTable;
/** /**
* CakeCarts\Model\Table\CartItemsTable Test Case * CakeCarts\Model\Table\CartItemsTable Test Case
*/ */
class CartItemsTableTest extends TestCase class CartItemsTableTest extends TestCase {
{
/** /**
* Test subject * Test subject
* *
* @var \CakeCarts\Model\Table\CartItemsTable * @var \CakeCarts\Model\Table\CartItemsTable
*/ */
protected $CartItems; protected $CartItems;
/** /**
* Fixtures * Fixtures
* *
* @var list<string> * @var list<string>
*/ */
protected array $fixtures = [ protected array $fixtures = [
'plugin.CakeCarts.CartItems', 'plugin.CakeCarts.CartItems',
'plugin.CakeCarts.Carts', 'plugin.CakeCarts.Carts',
]; ];
/** /**
* setUp method * setUp method
* *
* @return void * @return void
*/ */
protected function setUp(): void protected function setUp(): void {
{ parent::setUp();
parent::setUp(); $config = $this->getTableLocator()->exists('CartItems') ? [] : ['className' => CartItemsTable::class];
$config = $this->getTableLocator()->exists('CartItems') ? [] : ['className' => CartItemsTable::class]; $this->CartItems = $this->getTableLocator()->get('CartItems', $config);
$this->CartItems = $this->getTableLocator()->get('CartItems', $config); }
}
/** /**
* tearDown method * tearDown method
* *
* @return void * @return void
*/ */
protected function tearDown(): void protected function tearDown(): void {
{ unset($this->CartItems);
unset($this->CartItems);
parent::tearDown(); parent::tearDown();
} }
/** /**
* Test validationDefault method * Test validationDefault method
* *
* @return void
* @uses \CakeCarts\Model\Table\CartItemsTable::validationDefault() * @uses \CakeCarts\Model\Table\CartItemsTable::validationDefault()
* @return void
*/ */
public function testValidationDefault(): void public function testValidationDefault(): void {
{ $this->markTestIncomplete('Not implemented yet.');
$this->markTestIncomplete('Not implemented yet.'); }
}
/** /**
* Test buildRules method * Test buildRules method
* *
* @return void
* @uses \CakeCarts\Model\Table\CartItemsTable::buildRules() * @uses \CakeCarts\Model\Table\CartItemsTable::buildRules()
* @return void
*/ */
public function testBuildRules(): void public function testBuildRules(): void {
{ $this->markTestIncomplete('Not implemented yet.');
$this->markTestIncomplete('Not implemented yet.'); }
}
} }

View File

@@ -9,69 +9,66 @@ use CakeCarts\Model\Table\CartsTable;
/** /**
* CakeCarts\Model\Table\CartsTable Test Case * CakeCarts\Model\Table\CartsTable Test Case
*/ */
class CartsTableTest extends TestCase class CartsTableTest extends TestCase {
{
/** /**
* Test subject * Test subject
* *
* @var \CakeCarts\Model\Table\CartsTable * @var \CakeCarts\Model\Table\CartsTable
*/ */
protected $Carts; protected $Carts;
/** /**
* Fixtures * Fixtures
* *
* @var list<string> * @var list<string>
*/ */
protected array $fixtures = [ protected array $fixtures = [
'plugin.CakeCarts.Carts', 'plugin.CakeCarts.Carts',
// 'plugin.CakeCarts.Users', // 'plugin.CakeCarts.Users',
'plugin.CakeCarts.CartItems', 'plugin.CakeCarts.CartItems',
]; ];
/** /**
* setUp method * setUp method
* *
* @return void * @return void
*/ */
protected function setUp(): void protected function setUp(): void {
{ parent::setUp();
parent::setUp(); $config = $this->getTableLocator()->exists('Carts') ? [] : ['className' => CartsTable::class];
$config = $this->getTableLocator()->exists('Carts') ? [] : ['className' => CartsTable::class]; $this->Carts = $this->getTableLocator()->get('Carts', $config);
$this->Carts = $this->getTableLocator()->get('Carts', $config); }
}
/** /**
* tearDown method * tearDown method
* *
* @return void * @return void
*/ */
protected function tearDown(): void protected function tearDown(): void {
{ unset($this->Carts);
unset($this->Carts);
parent::tearDown(); parent::tearDown();
} }
/** /**
* Test validationDefault method * Test validationDefault method
* *
* @return void
* @uses \CakeCarts\Model\Table\CartsTable::validationDefault() * @uses \CakeCarts\Model\Table\CartsTable::validationDefault()
* @return void
*/ */
public function testValidationDefault(): void public function testValidationDefault(): void {
{ $this->markTestIncomplete('Not implemented yet.');
$this->markTestIncomplete('Not implemented yet.'); }
}
/** /**
* Test buildRules method * Test buildRules method
* *
* @return void
* @uses \CakeCarts\Model\Table\CartsTable::buildRules() * @uses \CakeCarts\Model\Table\CartsTable::buildRules()
* @return void
*/ */
public function testBuildRules(): void public function testBuildRules(): void {
{ $this->markTestIncomplete('Not implemented yet.');
$this->markTestIncomplete('Not implemented yet.'); }
}
} }

View File

@@ -7,20 +7,19 @@ use Cake\Core\Configure;
use Cake\Core\Plugin; use Cake\Core\Plugin;
use Cake\Database\Connection; use Cake\Database\Connection;
use Cake\Datasource\ConnectionManager; use Cake\Datasource\ConnectionManager;
use Cake\TestSuite\Fixture\SchemaLoader; use CakeCarts\CakeCartsPlugin;
use CakeProducts\CakeProductsPlugin;
use Migrations\TestSuite\Migrator; use Migrations\TestSuite\Migrator;
use TestApp\Controller\AppController; use TestApp\Controller\AppController;
if (!defined('DS')) { if (!defined('DS')) {
define('DS', DIRECTORY_SEPARATOR); define('DS', DIRECTORY_SEPARATOR);
} }
if (!defined('WINDOWS')) { if (!defined('WINDOWS')) {
if (DS === '\\' || substr(PHP_OS, 0, 3) === 'WIN') { if (DS === '\\' || substr(PHP_OS, 0, 3) === 'WIN') {
define('WINDOWS', true); define('WINDOWS', true);
} else { } else {
define('WINDOWS', false); define('WINDOWS', false);
} }
} }
define('PLUGIN_ROOT', dirname(__DIR__)); define('PLUGIN_ROOT', dirname(__DIR__));
@@ -45,69 +44,69 @@ require CORE_PATH . 'config/bootstrap.php';
require CAKE . 'functions.php'; require CAKE . 'functions.php';
Configure::write('App', [ Configure::write('App', [
'namespace' => 'TestApp', 'namespace' => 'TestApp',
'encoding' => 'UTF-8', 'encoding' => 'UTF-8',
'paths' => [ 'paths' => [
'testWebroot' => PLUGIN_ROOT . DS . 'tests' . DS . 'test_app' . DS . 'webroot' . DS, 'testWebroot' => PLUGIN_ROOT . DS . 'tests' . DS . 'test_app' . DS . 'webroot' . DS,
'webroot' => PLUGIN_ROOT . DS . 'webroot' . DS, 'webroot' => PLUGIN_ROOT . DS . 'webroot' . DS,
'templates' => [ 'templates' => [
PLUGIN_ROOT . DS . 'tests' . DS . 'test_app' . DS . 'templates' . DS, PLUGIN_ROOT . DS . 'tests' . DS . 'test_app' . DS . 'templates' . DS,
], ],
], ],
]); ]);
Configure::write('debug', true); Configure::write('debug', true);
Configure::write('CakeCarts', [ Configure::write('CakeCarts', [
'Carts' => [ 'Carts' => [
'table' => 'CakeCarts.Carts', 'table' => 'CakeCarts.Carts',
], ],
'CartItems' => [ 'CartItems' => [
'requirePricing' => false, 'requirePricing' => false,
], ],
'Users' => [ 'Users' => [
'user_id' => 'uuid', // integer or uuid 'user_id' => 'uuid', // integer or uuid
], ],
]); ]);
$cache = [ $cache = [
'default' => [ 'default' => [
'engine' => 'File', 'engine' => 'File',
'path' => CACHE, 'path' => CACHE,
], ],
'_cake_translations_' => [ '_cake_translations_' => [
'className' => 'File', 'className' => 'File',
'prefix' => 'crud_myapp_cake_core_', 'prefix' => 'crud_myapp_cake_core_',
'path' => CACHE . 'persistent/', 'path' => CACHE . 'persistent/',
'serialize' => true, 'serialize' => true,
'duration' => '+10 seconds', 'duration' => '+10 seconds',
], ],
'_cake_model_' => [ '_cake_model_' => [
'className' => 'File', 'className' => 'File',
'prefix' => 'crud_my_app_cake_model_', 'prefix' => 'crud_my_app_cake_model_',
'path' => CACHE . 'models/', 'path' => CACHE . 'models/',
'serialize' => 'File', 'serialize' => 'File',
'duration' => '+10 seconds', 'duration' => '+10 seconds',
], ],
]; ];
Cache::setConfig($cache); Cache::setConfig($cache);
class_alias(AppController::class, 'App\Controller\AppController'); class_alias(AppController::class, 'App\Controller\AppController');
Plugin::getCollection()->add(new \CakeCarts\CakeCartsPlugin()); Plugin::getCollection()->add(new CakeCartsPlugin());
Chronos::setTestNow(Chronos::now()); Chronos::setTestNow(Chronos::now());
if (!getenv('DB_URL')) { if (!getenv('DB_URL')) {
putenv('DB_URL=sqlite:///:memory:'); putenv('DB_URL=sqlite:///:memory:');
} }
ConnectionManager::setConfig('test', [ ConnectionManager::setConfig('test', [
'className' => Connection::class, 'className' => Connection::class,
'url' => getenv('DB_URL') ?: null, 'url' => getenv('DB_URL') ?: null,
'timezone' => 'UTC', 'timezone' => 'UTC',
'quoteIdentifiers' => false, 'quoteIdentifiers' => false,
'cacheMetadata' => true, 'cacheMetadata' => true,
]); ]);
/** /**