Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
bb19ce6
IBX-11083: Extending collaboration described in Developer Documentati…
julitafalcondusza Mar 17, 2026
bc86d84
Fix collaboration dependencies errors (#3093)
adriendupuis Mar 18, 2026
d3e6961
ShareCart*Controller.php: From attribute back to annotation
adriendupuis Mar 18, 2026
3798211
Check code samples with PHP7.4
adriendupuis Mar 18, 2026
167fbfc
Check code samples with PHP7.4
adriendupuis Mar 18, 2026
e7dd448
Check code samples with PHP7.4
adriendupuis Mar 18, 2026
eb85565
Back port code_samples/collaboration/src/ to PHP 7.4
adriendupuis Mar 18, 2026
2188ec6
Back port code_samples/collaboration/src/ to PHP 7.4
adriendupuis Mar 18, 2026
c83e6ff
Merge remote-tracking branch 'origin/4.6' into IBX-11083-4.6
adriendupuis Mar 19, 2026
b720a84
code_samples/collaboration/src/: fixes for PHP 8.2
adriendupuis Mar 19, 2026
33bdfc0
PHP & JS CS Fixes
adriendupuis Mar 19, 2026
c0e85c3
code_samples/collaboration/src/: fixes for PHP 7.4 property.promotedN…
adriendupuis Mar 19, 2026
97156cd
PHP & JS CS Fixes
adriendupuis Mar 19, 2026
af88d03
Merge branch '4.6' into IBX-11083-4.6
adriendupuis Mar 19, 2026
dd8d269
CartResolverDecorator: exception catch must set var before PHP 8
adriendupuis Mar 19, 2026
b831c03
PermissionResolverDecorator: exception catch must set var before PHP 8
adriendupuis Mar 19, 2026
34e6807
PermissionResolverDecorator: rm trailing coma supported since PHP 8.0
adriendupuis Mar 19, 2026
d87b7c6
Controller/: Finish moving back to annotations
adriendupuis Mar 19, 2026
fde1425
services.yaml: Use 4.6 default
adriendupuis Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions code_samples/collaboration/config/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.

# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:

services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.

# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'

# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones

App\Collaboration\Cart\Persistence\Gateway\DatabaseGateway:
arguments:
$connection: '@ibexa.persistence.connection'
tags:
- name: 'ibexa.collaboration.persistence.session.gateway'
discriminator: !php/const App\Collaboration\Cart\Persistence\Gateway\DatabaseGateway::DISCRIMINATOR

App\Collaboration\Cart\Persistence\Mapper:
tags:
- name: 'ibexa.collaboration.persistence.session.mapper'
discriminator: !php/const App\Collaboration\Cart\Persistence\Gateway\DatabaseGateway::DISCRIMINATOR

App\Collaboration\Cart\Mapper\CartSessionDomainMapper:
tags:
- name: 'ibexa.collaboration.service.session.domain.mapper'
type: App\Collaboration\Cart\Persistence\Values\CartSession

App\Collaboration\Cart\Mapper\CartSessionPersistenceMapper:
tags:
- name: 'ibexa.collaboration.service.session.persistence.mapper'
type: !php/const App\Collaboration\Cart\CartSessionType::IDENTIFIER

App\Collaboration\Cart\PermissionResolverDecorator:
decorates: Ibexa\Contracts\ProductCatalog\PermissionResolverInterface

App\Collaboration\Cart\CartResolverDecorator:
decorates: Ibexa\Contracts\Cart\CartResolverInterface
7 changes: 7 additions & 0 deletions code_samples/collaboration/ibexa_collaboration_cart.mysql.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CREATE TABLE ibexa_collaboration_cart (
id INT NOT NULL PRIMARY KEY,
cart_identifier VARCHAR(255) NOT NULL,
CONSTRAINT ibexa_collaboration_cart_ibexa_collaboration_id_fk
FOREIGN KEY (id) REFERENCES ibexa_collaboration (id)
ON DELETE CASCADE
) COLLATE = utf8mb4_general_ci;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CREATE TABLE ibexa_collaboration_cart (
id INTEGER NOT NULL PRIMARY KEY,
cart_identifier VARCHAR(255) NOT NULL,

Check warning on line 3 in code_samples/collaboration/ibexa_collaboration_cart.postgresql.sql

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use VARCHAR2 instead of VARCHAR.

See more on https://sonarcloud.io/project/issues?id=ezsystems_developer-documentation&issues=AZ0AtbXEjfSg5Wau5Ccq&open=AZ0AtbXEjfSg5Wau5Ccq&pullRequest=3094
CONSTRAINT ibexa_collaboration_cart_ibexa_collaboration_id_fk
FOREIGN KEY (id) REFERENCES ibexa_collaboration (id)
ON DELETE CASCADE
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php declare(strict_types=1);

namespace App\Collaboration\Cart;

use Ibexa\Contracts\Cart\CartResolverInterface;
use Ibexa\Contracts\Cart\Value\CartInterface;
use Ibexa\Contracts\Collaboration\SessionServiceInterface;
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException;
use Ibexa\Contracts\Core\Repository\Values\User\User;
use Symfony\Component\HttpFoundation\RequestStack;

final class CartResolverDecorator implements CartResolverInterface
{
private CartResolverInterface $innerCartResolver;

private SessionServiceInterface $sessionService;

private RequestStack $requestStack;

public function __construct(
CartResolverInterface $innerCartResolver,
SessionServiceInterface $sessionService,
RequestStack $requestStack
) {
$this->innerCartResolver = $innerCartResolver;
$this->sessionService = $sessionService;
$this->requestStack = $requestStack;
}

public function resolveCart(?User $user = null): CartInterface
{
if ($this->hasSharedCart()) {
return $this->getSharedCart() ?? $this->innerCartResolver->resolveCart($user);
}

return $this->innerCartResolver->resolveCart($user);
}

private function getSharedCart(): ?CartInterface
{
try {
$session = $this->sessionService->getSessionByToken(
$this->requestStack->getSession()->get(PermissionResolverDecorator::COLLABORATION_SESSION_ID)
);

if (!$session instanceof CartSession) {
return null;
}

return $session->getCart();
} catch (NotFoundException|UnauthorizedException $exception) {
return null;
}
}

private function hasSharedCart(): bool
{
return $this->requestStack->getSession()->has(PermissionResolverDecorator::COLLABORATION_SESSION_ID);
}
}
34 changes: 34 additions & 0 deletions code_samples/collaboration/src/Collaboration/Cart/CartSession.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php declare(strict_types=1);

namespace App\Collaboration\Cart;

use DateTimeInterface;
use Ibexa\Contracts\Cart\Value\CartInterface;
use Ibexa\Contracts\Collaboration\Participant\ParticipantCollectionInterface;
use Ibexa\Contracts\Collaboration\Session\AbstractSession;
use Ibexa\Contracts\Core\Repository\Values\User\User;

final class CartSession extends AbstractSession
{
private CartInterface $cart;

public function __construct(
int $id,
CartInterface $cart,
string $token,
User $owner,
ParticipantCollectionInterface $participants,
bool $isActive,
bool $hasPublicLink,
DateTimeInterface $createdAt,
DateTimeInterface $updatedAt
) {

Check warning on line 25 in code_samples/collaboration/src/Collaboration/Cart/CartSession.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This function has 9 parameters, which is greater than the 7 authorized.

See more on https://sonarcloud.io/project/issues?id=ezsystems_developer-documentation&issues=AZ0AtbWhjfSg5Wau5Ccn&open=AZ0AtbWhjfSg5Wau5Ccn&pullRequest=3094
$this->cart = $cart;
parent::__construct($id, $token, $owner, $participants, $isActive, $hasPublicLink, $createdAt, $updatedAt);
}

public function getCart(): CartInterface
{
return $this->cart;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php declare(strict_types=1);

namespace App\Collaboration\Cart;

use Ibexa\Contracts\Cart\Value\CartInterface;
use Ibexa\Contracts\Collaboration\Session\AbstractSessionCreateStruct;

final class CartSessionCreateStruct extends AbstractSessionCreateStruct
{
private CartInterface $cart;

public function __construct(CartInterface $cart)
{
$this->cart = $cart;
parent::__construct();
}

public function getCart(): CartInterface
{
return $this->cart;
}

public function setCart(CartInterface $cart): void
{
$this->cart = $cart;
}

public function getType(): string
{
return CartSessionType::IDENTIFIER;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php declare(strict_types=1);

namespace App\Collaboration\Cart;

use Ibexa\Contracts\Collaboration\Session\SessionScopeInterface;

final class CartSessionType implements SessionScopeInterface
{
public const SCOPE_VIEW = 'view';
public const SCOPE_EDIT = 'edit';

public const IDENTIFIER = 'cart';

private function __construct()
{
// This class is not intended to be instantiated
}

public function getDefaultScope(): string
{
return self::SCOPE_VIEW;
}

public function isValidScope(string $scope): bool
{
return in_array($scope, $this->getScopes(), true);
}

public function getScopes(): array
{
return [
self::SCOPE_VIEW,
self::SCOPE_EDIT,
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php declare(strict_types=1);

namespace App\Collaboration\Cart;

use Ibexa\Contracts\Collaboration\Session\AbstractSessionUpdateStruct;

final class CartSessionUpdateStruct extends AbstractSessionUpdateStruct
{
public function getType(): string
{
return CartSessionType::IDENTIFIER;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php declare(strict_types=1);

namespace App\Collaboration\Cart\Mapper;

use Ibexa\Contracts\Cart\CartServiceInterface;
use Ibexa\Contracts\Cart\Value\CartInterface;
use Ibexa\Contracts\Core\Repository\Repository;
use Ibexa\Core\Repository\ProxyFactory\ProxyGeneratorInterface;
use ProxyManager\Proxy\LazyLoadingInterface;

final class CartProxyMapper implements CartProxyMapperInterface
{
private Repository $repository;

private CartServiceInterface $cartService;

private ProxyGeneratorInterface $proxyGenerator;

public function __construct(
Repository $repository,
CartServiceInterface $cartService,
ProxyGeneratorInterface $proxyGenerator
) {
$this->repository = $repository;
$this->cartService = $cartService;
$this->proxyGenerator = $proxyGenerator;
}

public function createCartProxy(string $identifier): CartInterface
{
$initializer = function (
&$wrappedObject,
LazyLoadingInterface $proxy,

Check warning on line 33 in code_samples/collaboration/src/Collaboration/Cart/Mapper/CartProxyMapper.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unused function parameter "$proxy".

See more on https://sonarcloud.io/project/issues?id=ezsystems_developer-documentation&issues=AZ0AtbR5jfSg5Wau5Cck&open=AZ0AtbR5jfSg5Wau5Cck&pullRequest=3094
$method,

Check warning on line 34 in code_samples/collaboration/src/Collaboration/Cart/Mapper/CartProxyMapper.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unused function parameter "$method".

See more on https://sonarcloud.io/project/issues?id=ezsystems_developer-documentation&issues=AZ0AtbR5jfSg5Wau5Ccl&open=AZ0AtbR5jfSg5Wau5Ccl&pullRequest=3094
array $parameters,

Check warning on line 35 in code_samples/collaboration/src/Collaboration/Cart/Mapper/CartProxyMapper.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unused function parameter "$parameters".

See more on https://sonarcloud.io/project/issues?id=ezsystems_developer-documentation&issues=AZ0AtbR5jfSg5Wau5Ccm&open=AZ0AtbR5jfSg5Wau5Ccm&pullRequest=3094
&$initializer
) use ($identifier): bool {
$initializer = null;
$wrappedObject = $this->repository->sudo(fn (): CartInterface => $this->cartService->getCart($identifier));

return true;
};

return $this->proxyGenerator->createProxy(CartInterface::class, $initializer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php declare(strict_types=1);

namespace App\Collaboration\Cart\Mapper;

use Ibexa\Contracts\Cart\Value\CartInterface;

interface CartProxyMapperInterface
{
public function createCartProxy(string $identifier): CartInterface;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php declare(strict_types=1);

namespace App\Collaboration\Cart\Mapper;

use App\Collaboration\Cart\CartSession;
use Ibexa\Collaboration\Mapper\Domain\ParticipantCollectionDomainMapperInterface;
use Ibexa\Collaboration\Mapper\Domain\SessionDomainMapperInterface;
use Ibexa\Collaboration\Mapper\Domain\UserProxyDomainMapperInterface;
use Ibexa\Collaboration\Persistence\Values\AbstractSession as SessionData;
use Ibexa\Contracts\Collaboration\Session\SessionInterface;

/**
* @template-implements \Ibexa\Collaboration\Mapper\Domain\SessionDomainMapperInterface<
* \App\Collaboration\Cart\Persistence\Values\CartSession
* >
*/
final class CartSessionDomainMapper implements SessionDomainMapperInterface
{
private CartProxyMapperInterface $cartProxyMapper;

private UserProxyDomainMapperInterface $userDomainMapper;

private ParticipantCollectionDomainMapperInterface $participantCollectionDomainMapper;

public function __construct(
CartProxyMapperInterface $cartProxyMapper,
UserProxyDomainMapperInterface $userDomainMapper,
ParticipantCollectionDomainMapperInterface $participantCollectionDomainMapper
) {
$this->cartProxyMapper = $cartProxyMapper;
$this->userDomainMapper = $userDomainMapper;
$this->participantCollectionDomainMapper = $participantCollectionDomainMapper;
}

/**
* @param \App\Collaboration\Cart\Persistence\Values\CartSession $data
*/
public function fromPersistence(SessionData $data): SessionInterface
{
return new CartSession(
$data->getId(),
$this->cartProxyMapper->createCartProxy($data->getCartIdentifier()),
$data->getToken(),
$this->userDomainMapper->createUserProxy($data->getOwnerId()),
$this->participantCollectionDomainMapper->createParticipantCollectionProxy($data->getId()),
$data->isActive(),
$data->hasPublicLink(),
$data->getCreatedAt(),
$data->getUpdatedAt(),
);
}
}
Loading
Loading