comsa / sulu-shopping-cart
Installs: 237
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Forks: 0
Type:symfony-bundle
pkg:composer/comsa/sulu-shopping-cart
Requires
- php: ^8.0
- dantleech/phpcr-migrations-bundle: ^1.2
- doctrine/orm: >=2.0
- mollie/mollie-api-php: ^2.0
- phpoffice/phpspreadsheet: ^1.21
- sulu/sulu: ^2.0
- symfony/config: ^4.3 || ^5.0
- symfony/dependency-injection: ^4.3 || ^5.0
- symfony/event-dispatcher: ^4.3 || ^5.0
- symfony/form: ^4.3 || ^5.0
- symfony/http-foundation: ^4.3 || ^5.0
- symfony/http-kernel: ^4.3 || ^5.0
- symfony/mailer: ^4.3 || ^5.0
- twig/twig: >=2.0 || ^3.0
Requires (Dev)
- jackalope/jackalope-doctrine-dbal: ^1.3.4
- jackalope/jackalope-jackrabbit: ^1.3
- phpcr/phpcr-shell: ^1.1
- dev-master
- 4.3.1
- 4.3.0
- 4.2.2
- 4.2.1
- 4.2.0
- 4.1.1
- 4.1.0
- 4.0.0
- 3.3.1
- 3.3.0
- 3.2.11
- 3.2.10
- 3.2.9
- 3.2.8
- 3.2.7
- 3.2.6
- 3.2.5
- 3.2.4
- 3.2.3
- 3.2.2
- 3.2.1
- 3.2.0
- 3.1.17
- 3.1.16
- 3.1.15
- 3.1.14
- 3.1.13
- 3.1.12
- 3.1.11
- 3.1.10
- 3.1.9
- 3.1.8
- 3.1.7
- 3.1.6
- 3.1.5
- 3.1.4
- 3.1.3
- 3.1.2
- 3.1.1
- 3.1.0
- 3.0.2
- 3.0.1
- 3.0.0
- 2.4.8
- 2.4.7
- 2.4.6
- 2.4.5
- 2.4.4
- 2.4.3
- 2.4.2
- 2.4.1
- 2.4.0
- 2.3.1
- 2.3.0
- 2.2.1
- 2.2.0
- 2.1.3
- 2.1.2
- 2.1.1
- 2.1.0
- 2.0.0
- 1.1.1
- 1.1.0
- 1.0.0
This package is auto-updated.
Last update: 2025-10-19 21:35:46 UTC
README
composer req comsa/sulu-shopping-cart
Add to assets/admin/package.json:
"sulu-shopping-cart-bundle": "file:node_modules/@sulu/vendor/comsa/sulu-shopping-cart/Resources/js"
Run npm install
Add it to assets/admin/app.js:
import 'sulu-shopping-cart-bundle/admin'
And build it using npm run build, this might take a while :)
Setup the frontend js: Add it to package.json:
"sulu-shopping-cart-bundle": "file:../../vendor/comsa/sulu-shopping-cart/Resources/js"
Add it to index.js:
import 'sulu-shopping-cart-bundle/website';
And build it using npm run build or use npm run dev in development
Add routes to both routes_admin.yaml and routes_website.yaml
In: config/packages/doctrine.yaml
doctrine:
  orm:
    mappings:
      SuluShoppingCartBundle:
        is_bundle: true
        type: attribute
        dir: '/Entity'
        prefix: 'Comsa\SuluShoppingCart\Entity'
        alias: SuluShoppingCart
In: config/routes_admin.yaml
sulu_shopping_cart_admin:
  type: rest
  resource: "@SuluShoppingCartBundle/Resources/config/routes/admin.yaml"
  prefix: /admin/api
sulu_shopping_cart_admin_controller:
  resource: "@SuluShoppingCartBundle/Resources/config/routes/adminController.yaml"
In: config/routes_website.yaml
sulu_shopping_cart_website:
  resource: "@SuluShoppingCartBundle/Resources/config/routes/website.yaml"
Default configuration
Adjust to your wishes
parameters:
  #Configure the correct value in the .env file
  comsa_sulu_shopping_mollie_api_key: '%env(COMSA_SC_MOLLIE_API_KEY)%'
Make sure your product template is called "comsa_product".
<property name="product" type="single_product_selection">
    <meta>
        <title lang="en">Product</title>
        <title lang="nl">Product</title>
    </meta>
</property>
Add the following to your product template, this will render the add to cart part:
{{ render(controller('Comsa\\SuluShoppingCart\\Controller\\CartController::addToCart', {'uuid': id})) }}
To add the cart somewhere, use the following, you can overwrite this template from within your own templates:
{% include '@SuluShoppingCart/cart-small.html.twig' %}
Extending Entities
Create an CartItem.php class in src/entity. 
<?php
declare(strict_types=1);
namespace App\Entity;
use Comsa\SuluShoppingCart\Entity\CartItem as BaseCartItem;
use Doctrine\ORM\Mapping\Entity;
#[Entity()]
class CartItem extends BaseCartItem
{
}
Create an Order.php in src/entity:
<?php
namespace App\Entity;
use Comsa\SuluShoppingCart\Entity\Order as BaseOrder;
use Doctrine\ORM\Mapping as ORM;
/**
 * @ORM\Entity
 */
class Order extends BaseOrder
{
}
Here you can define custom properties. 
For example:
<?php
namespace App\Entity;
use Comsa\SuluShoppingCart\Entity\CartItem as BaseCartItem;
use Doctrine\ORM\Mapping as ORM;
/**
 * @ORM\Entity
 */
class CartItem extends BaseCartItem
{
      /**
     * @ORM\Column(type="text", length=6000, nullable=true)
     */
    private $accessoryText;
    public function getAccessoryText(): ?string
    {
        return $this->accessoryText;
    }
    public function setAccessoryText(?string $accessoryText)
    {
        $this->accessoryText = $accessoryText;
    }
}
Make sure to apply the Symfony coding standards and the namespaces are correct as shown above.
After extending your entities update your database:
php bin/console doctrine:schema:update -f
Extending Forms
Extend the basic forms with your custom properties by extending the forms. 
For example:
<?php
namespace App\Form\Extension;
use Comsa\SuluShoppingCart\Form\AddToCartType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
class AddToCartTypeExtension extends AbstractTypeExtension
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
       $builder->add("accessoryText", TextType::class);
    }
    public static function getExtendedTypes(): iterable
    {
        return [
          AddToCartType::class
        ];
    }
}
Make sure the namespaces are correct as shown above.
Loading Settings
To configure paymentmethods and shipmentmethods, settings need to be loaded.
Copy and paste the following inside AppFixtures.php in App\DataFixtures
<?php
namespace App\DataFixtures;
use Comsa\SuluShoppingCart\DataFixtures\AppSeed;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Persistence\ObjectManager;
class AppFixtures extends Fixture implements DependentFixtureInterface
{
    public function load(ObjectManager $manager)
    {
    }
    public function getDependencies(): array
    {
        return [
            AppSeed::class
        ];
    }
}
Run the following command to load in the settings:
php bin/console doctrine:fixtures:load --append
Make sure they are configured correctly to your needs.
Translations
Extended properties are automatically rendered in the order summary.
Translations can be made inside the translations/sulu folder in the root of the app. Name of the file should be admin.{locale}.yaml.
For Example:
properties:
  accessoryText: "Tekst op kaart"
Make sure to enter all custom properties under the properties namespace as shown above.
Templates (optional)
Product overview XML:
<?xml version="1.0" ?>
<type name="product_overview" xmlns="http://schemas.sulu.io/template/template"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:xi="http://www.w3.org/2001/XInclude"
      xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.sulu.io/template/template-1.0.xsd">
    <meta>
        <title lang="en">Product Overview</title>
        <title lang="nl">Product Overzicht</title>
    </meta>
    <properties>
        <property name="products" type="smart_content">
            <meta>
                <title lang="en">Products</title>
                <title lang="nl">Producten</title>
            </meta>
            <params>
                <param name="provider" value="pages"/>
                <param name="max_per_page" value="16"/>
                <param name="page_parameter" value="page"/>
                <param name="properties" type="collection">
                    <param name="title" value="title"/>
                    <param name="text" value="text"/>
                    <param name="product" value="product"/>
                    <param name="images" value="images"/>
                    <param name="rating" value="rating"/>
                    <param name="description" value="description"/>
                    <param name="imageFormat" value="imageFormat"/>
                    <param name="location" value="location"/>
                    <param name="sold" value="sold"/>
                    <param name="licence" value="licence"/>
                    <param name="price" value="price"/>
                    <param name="btntext" value="btntext"/>
                    <param name="status" value="status"/>
                </param>
            </params>
        </property>
    </properties>
</type>
Product overview TWIG
<section class="product-overview">
  <div class="row gx-5">
    {% for page in block.products %}
      {% set product = page.product %}
      {% if product.thumbnail %}
        {% set thumbnail = sulu_resolve_media(product.thumbnail, app.request.locale) %}
      {% else %}
        {% set thumbnail = false %}
      {% endif %}
      <article class="col-12 col-sm-6 col-xl-3">
        <div class="card mx-auto my-2">
          <div class="image-wrapper">
            {% if thumbnail %}
              <img src="{{ thumbnail.formats['x380_default'] }}" class="card-img-top" alt="{{ product.title }}" title="{{ product.title }}">
            {% else %}
              <p>{{ 'comsa_sulu_shopping_cart.no_thumbnail'|trans }}</p>
            {% endif %}
          </div>
          <div class="card-body">
            <h5 class="card-title">{{ product.title }}</h5>
            <h6 class="card-title">€{{ product.price|number_format(2) }}</h6>
            <a href="{{ sulu_content_path(page.url) }}" class="btn btn-primary">Bekijk product</a>
          </div>
        </div>
      </article>
    {% endfor %}
    {% if pagination is defined and pagination == true %}
      <nav class="mt-3" aria-label="{{ 'label.paginering'|trans|capitalize }} ">
        {% set page = viewLink.page %}
        <ul class="pagination {% if page -1 < 1 %} justify-content-end {% endif %}">
          {% if page-1 >= 1 %}
            <li class="page-item">
              <a class="page-link" href="{{ sulu_content_path(content.url) }}?page={{ page-1 }}" aria-label="Previous">
                <span aria-hidden="true">«</span>
                <span>{{ 'label.previous'|trans|capitalize }}</span>
              </a>
            </li>
          {% endif %}
          {% if viewLink.hasNextPage %}
            <li class="page-item">
              <a class="page-link" href="{{ sulu_content_path(content.url) }}?page={{ page+1 }}" aria-label="Next">
                <span>{{ 'label.next'|trans|capitalize }}</span>
                <span aria-hidden="true">»</span>
              </a>
            </li>
          {% endif %}
        </ul>
      </nav>
    {% endif %}
  </div>
</section>
Product Page HTML
{% extends "base.html.twig" %}
{% block content %}
  {% set product = content.product %}
  <div class="product-details-wrapper">
    <div class="container">
      <div class="row py-5">
        {% if product.thumbnail %}
          <div class="col-md-6">
            {% set thumbnail = sulu_resolve_media(product.thumbnail, app.request.locale) %}
            <div class="product-thumbnail-wrapper">
              <img class="product-thumbnail" src="{{ thumbnail.formats['default'] }}" alt="{{ product.title }}"
                   title="{{ product.title }}">
            </div>
          </div>
        {% else %}
          <div class="col-md-6">
            <div class="product-thumbnail-wrapper img-thumbnail d-flex align-items-center justify-content-center">
              <p>Geen foto beschikbaar</p>
            </div>
          </div>
        {% endif %}
        <div class="col-md-6 mt-3 d-flex">
          <div class="mx-auto product-info">
            <h1>{{ product.title }}</h1>
            {% if product.code %}
              <p class="text-muted " style="font-size: 0.8rem;"> {{ 'comsa_sulu_shopping_cart.code'|trans }}
                : {{ product.code }}</p>
            {% endif %}
            {% if product.description %}
              {{ product.description|raw }}
            {% else %}
              {{ 'comsa_sulu_shopping_cart.no_description'|trans }}
            {% endif %}
            <div class="products">
              <p class="price">
                <span>€{{ product.price|number_format(2) }}</span>
              </p>
            </div>
            {% if product.followStock %}
              {% if product.stock > 0 %}
                {{ render(controller('Comsa\\SuluShoppingCart\\Controller\\CartController::addToCart', {'uuid': id})) }}
              {% else %}
                <div class="alert alert-danger" role="alert">
                  {{ 'comsa_sulu_shopping_cart.not_available'|trans }}
                </div>
              {% endif %}
            {% else %}
              {{ render(controller('Comsa\\SuluShoppingCart\\Controller\\CartController::addToCart', {'uuid': id})) }}
            {% endif %}
          </div>
        </div>
        {% include 'blocks/all.html.twig' with { 'blocks': content.extendedBlocks } %}
      </div>
    </div>
  </div>
{% endblock %}
Importing Products
Products can be imported with the following command:
comsa:shopping:import-products <inputfile> <parent-page-id> <webspace-key> <locale>
The arguments represent the following:
InputFile Requirements
- File must be an Xlsx file.
- Inputfile argument must have the full path to file (eg. public/uploads/files/products.xlsx).
- Products must start on the second row.
- File must have the following structure:
| A | B | C | D | E | F | G | H | |
|---|---|---|---|---|---|---|---|---|
| 1 | Product Code | Category | Title | Description | Price | Unit | Follow Stock | Stock | 
| 2 | PRODUCTS START | |||||||
InputFile Field Requirements
| Field | Required | Requirements | 
|---|---|---|
| Product Code | False | Max length: 255 | 
| Category | True | / | 
| Title | True | Max length: 255 | 
| Description | False | Max length: 65534 | 
| Price | True | Must be in euro (eg. €22,70) | 
| Unit | True | Must be: 
 | 
| Follow Stock | True | Must be "yes" or "no" | 
| Stock | False | Only integers |