Skip to content

Commit

Permalink
feature(related-products): Add ability to link products to articles
Browse files Browse the repository at this point in the history
  • Loading branch information
Yousef Chanan committed Jun 18, 2016
1 parent 50bc325 commit 541e13b
Show file tree
Hide file tree
Showing 12 changed files with 219 additions and 20 deletions.
3 changes: 3 additions & 0 deletions Command/InstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,14 @@ private function createArticleTables($manager)

$queries = [
'CREATE TABLE webburza_sylius_article (id INT AUTO_INCREMENT NOT NULL, category_id INT DEFAULT NULL, published TINYINT(1) NOT NULL, featured TINYINT(1) NOT NULL, published_at DATETIME DEFAULT NULL, created_at DATETIME DEFAULT NULL, updated_at DATETIME DEFAULT NULL, INDEX IDX_9FD397A312469DE2 (category_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB',
'CREATE TABLE webburza_sylius_article_product (product_id INT NOT NULL, article_id INT NOT NULL, INDEX IDX_2AB0C4534584665A (product_id), UNIQUE INDEX UNIQ_2AB0C4537294869C (article_id), PRIMARY KEY(product_id, article_id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB',
'CREATE TABLE webburza_sylius_article_category (id INT AUTO_INCREMENT NOT NULL, published TINYINT(1) NOT NULL, created_at DATETIME DEFAULT NULL, updated_at DATETIME DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB',
'CREATE TABLE webburza_sylius_article_translation (id INT AUTO_INCREMENT NOT NULL, translatable_id INT NOT NULL, title VARCHAR(255) DEFAULT NULL, slug VARCHAR(255) DEFAULT NULL, lead LONGTEXT DEFAULT NULL, content LONGTEXT DEFAULT NULL, meta_keywords LONGTEXT DEFAULT NULL, meta_description LONGTEXT DEFAULT NULL, active TINYINT(1) NOT NULL, locale VARCHAR(255) NOT NULL, INDEX IDX_B49ACC0B2C2AC5D3 (translatable_id), UNIQUE INDEX webburza_sylius_article_translation_uniq_trans (translatable_id, locale), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB',
'CREATE TABLE webburza_sylius_article_image (id INT AUTO_INCREMENT NOT NULL, article_id INT DEFAULT NULL, path VARCHAR(255) NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME DEFAULT NULL, UNIQUE INDEX UNIQ_710599397294869C (article_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB',
'CREATE TABLE webburza_sylius_article_category_translation (id INT AUTO_INCREMENT NOT NULL, translatable_id INT NOT NULL, title VARCHAR(255) DEFAULT NULL, slug VARCHAR(255) DEFAULT NULL, locale VARCHAR(255) NOT NULL, INDEX IDX_1B9F69192C2AC5D3 (translatable_id), UNIQUE INDEX webburza_sylius_article_category_translation_uniq_trans (translatable_id, locale), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB',
'ALTER TABLE webburza_sylius_article ADD CONSTRAINT FK_9FD397A312469DE2 FOREIGN KEY (category_id) REFERENCES webburza_sylius_article_category (id)',
'ALTER TABLE webburza_sylius_article_product ADD CONSTRAINT FK_2AB0C4534584665A FOREIGN KEY (product_id) REFERENCES webburza_sylius_article (id)',
'ALTER TABLE webburza_sylius_article_product ADD CONSTRAINT FK_2AB0C4537294869C FOREIGN KEY (article_id) REFERENCES sylius_product (id)',
'ALTER TABLE webburza_sylius_article_translation ADD CONSTRAINT FK_B49ACC0B2C2AC5D3 FOREIGN KEY (translatable_id) REFERENCES webburza_sylius_article (id) ON DELETE CASCADE',
'ALTER TABLE webburza_sylius_article_image ADD CONSTRAINT FK_710599397294869C FOREIGN KEY (article_id) REFERENCES webburza_sylius_article (id) ON DELETE CASCADE',
'ALTER TABLE webburza_sylius_article_category_translation ADD CONSTRAINT FK_1B9F69192C2AC5D3 FOREIGN KEY (translatable_id) REFERENCES webburza_sylius_article_category (id) ON DELETE CASCADE'
Expand Down
2 changes: 2 additions & 0 deletions Command/UninstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ private function removeArticleTables($manager)
}

$queries = [
'ALTER TABLE webburza_sylius_article_product DROP FOREIGN KEY FK_2AB0C4534584665A',
'ALTER TABLE webburza_sylius_article_translation DROP FOREIGN KEY FK_B49ACC0B2C2AC5D3',
'ALTER TABLE webburza_sylius_article_image DROP FOREIGN KEY FK_710599397294869C',
'ALTER TABLE webburza_sylius_article DROP FOREIGN KEY FK_9FD397A312469DE2',
'ALTER TABLE webburza_sylius_article_category_translation DROP FOREIGN KEY FK_1B9F69192C2AC5D3',
'DROP TABLE webburza_sylius_article',
'DROP TABLE webburza_sylius_article_product',
'DROP TABLE webburza_sylius_article_category',
'DROP TABLE webburza_sylius_article_translation',
'DROP TABLE webburza_sylius_article_image',
Expand Down
59 changes: 59 additions & 0 deletions Entity/Article.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace Webburza\Sylius\ArticleBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Sylius\Component\Core\Model\ProductInterface;
use Sylius\Component\Resource\Model\TranslatableTrait;
use Symfony\Component\Validator\Constraints as Assert;
use Webburza\Sylius\ArticleBundle\Model\ArticleCategoryInterface;
Expand Down Expand Up @@ -82,12 +84,28 @@ class Article implements ArticleInterface
*/
protected $category;

/**
* @var ProductInterface[]
*
* @ORM\ManyToMany(targetEntity="Sylius\Component\Core\Model\ProductInterface")
* @ORM\JoinTable(name="webburza_sylius_article_product",
* joinColumns={
* @ORM\JoinColumn(name="product_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* @ORM\JoinColumn(name="article_id", referencedColumnName="id", unique=true)
* }
* )
*/
protected $products;

/**
* Article constructor.
*/
public function __construct()
{
$this->initializeTranslationsCollection();
$this->products = new ArrayCollection();
}

/**
Expand Down Expand Up @@ -291,4 +309,45 @@ public function setCategory(ArticleCategoryInterface $category)

return $this;
}

/**
* @return mixed
*/
public function getProducts()
{
return $this->products;
}

/**
* @param ArrayCollection $products
* @return ArticleInterface
*/
public function setProducts(ArrayCollection $products)
{
$this->products = $products;

return $this;
}

/**
* @param ProductInterface $product
* @return ArticleInterface
*/
public function addProduct(ProductInterface $product)
{
$this->products->add($product);

return $this;
}

/**
* @param ProductInterface $product
* @return ArticleInterface
*/
public function removeProduct(ProductInterface $product)
{
$this->products->removeElement($product);

return $this;
}
}
8 changes: 8 additions & 0 deletions Form/Type/ArticleType.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ public function buildForm(FormBuilderInterface $builder, array $options)
'label' => 'webburza.sylius.article.label.category'
]);

$builder->add('products', 'webburza_article_product_choice', [
'label' => 'webburza.sylius.article.label.products',
'attr' => [
'size' => 10
],
'multiple' => true
]);

$builder->add('publishedAt', Type\DateTimeType::class, [
'label' => 'webburza.sylius.article.label.published_at',
'required' => false,
Expand Down
70 changes: 70 additions & 0 deletions Form/Type/ProductChoiceType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace Webburza\Sylius\ArticleBundle\Form\Type;

use Sylius\Component\Resource\Repository\RepositoryInterface;
use Symfony\Bridge\Doctrine\Form\DataTransformer\CollectionToArrayTransformer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ProductChoiceType extends AbstractType
{
/**
* @var RepositoryInterface
*/
protected $repository;

/**
* @param RepositoryInterface $repository
*/
public function __construct(RepositoryInterface $repository)
{
$this->repository = $repository;
}

/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);

if (true === $options['multiple']) {
$builder->addViewTransformer(new CollectionToArrayTransformer(), true);
}
}

/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$choiceList = new ObjectChoiceList($this->repository->findAll(), 'name', [], null, 'id');

$resolver
->setDefaults([
'choice_list' => $choiceList,
'label' => 'webburza.sylius.article.label.products',
'empty_value' => 'webburza.sylius.article.label.choose_products'
])
;
}

/**
* {@inheritdoc}
*/
public function getParent()
{
return 'choice';
}

/**
* {@inheritdoc}
*/
public function getName()
{
return 'webburza_article_product_choice';
}
}
1 change: 1 addition & 0 deletions Resources/config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ doctrine:
Webburza\Sylius\ArticleBundle\Model\ArticleImageInterface: %webburza.model.article_image.class%
Webburza\Sylius\ArticleBundle\Model\ArticleCategoryInterface: %webburza.model.article_category.class%
Webburza\Sylius\ArticleBundle\Model\ArticleInterface: %webburza.model.article.class%
Sylius\Component\Core\Model\ProductInterface: %sylius.model.product.class%

webburza_sylius_article:
file_browser:
Expand Down
6 changes: 6 additions & 0 deletions Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,9 @@ services:
tags:
- { name: form.type, alias: webburza_article_category_choice }
arguments: [@webburza.repository.article_category]

webburza.sylius.article.form.type.product_choice:
class: Webburza\Sylius\ArticleBundle\Form\Type\ProductChoiceType
tags:
- { name: form.type, alias: webburza_article_product_choice }
arguments: [@sylius.repository.product]
3 changes: 3 additions & 0 deletions Resources/translations/messages.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ webburza:
no_results: No articles found.
translations: Translations
general_info: General info
related_products: Related products
back_to_all: back to all articles
delete_image: Delete image
label:
Expand All @@ -35,6 +36,8 @@ webburza:
featured: Featured?
created_at: Created At
updated_at: Updated At
products: Related products
choose_products: Choose products...
category: Category
choose_category: Choose a category...
file: File
Expand Down
3 changes: 3 additions & 0 deletions Resources/translations/messages.hr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ webburza:
no_results: Nije pronađen niti jedan članak.
translations: Prijevodi
general_info: Općenite informacije
related_products: Povezani proizvodi
back_to_all: natrag na sve članke
delete_image: Izbriši sliku
label:
Expand All @@ -35,6 +36,8 @@ webburza:
featured: Istaknuto?
created_at: Datum kreiranja
updated_at: Datum ažuriranja
products: Povezani proizvodi
choose_products: Odaberite proizvode...
choose_category: Kategorija
choose_category: Odaberite kategoriju...
file: Datoteka
Expand Down
25 changes: 25 additions & 0 deletions Resources/views/Backend/Article/show.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,31 @@
<div class="panel-heading">{{ article.title }}</div>
<div class="panel-body">{{ article.content | raw }}</div>
</div>

{% if article.products is not empty %}
<table class="table table-bordered">
<thead>
<tr>
<th colspan="2">{{ 'webburza.sylius.article.related_products'|trans }}</th>
</tr>
</thead>
<tbody>
{% for product in article.products %}
<tr>
<td width="10%">
<a href="{{ path('sylius_backend_product_show', {'id': product.id}) }}" class="pull-left"
title="{{ 'sylius.ui.view_product_details'|trans({'%product%': product.name})|escape('html_attr') }}">
<img src="{{ product.image ? product.image.path|imagine_filter('sylius_small') : 'http://placehold.it/120x90' }}" alt="" class="imgmedia-object" />
</a>
</td>
<td>
<a href="{{ path('sylius_backend_product_show', {id: product.id}) }}">{{ product.name }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>

<div class="col-md-6">
Expand Down
53 changes: 36 additions & 17 deletions Resources/views/Frontend/Article/macros.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,43 @@
{% endfor %}
{% endmacro %}

{% macro articleSidebar(categories, currentCategory, articleView) %}
{% macro articleSidebar(categories, currentCategory, products, articleView) %}
<div class="col-md-3{{ articleView ? ' col-md-offset-2' : '' }}">
<ul class="nav">
<li class="first">
<span class="nav-header">
<span>{{ 'webburza.sylius.article_category.index_header' | trans }}</span>
</span>

<ul class="nav nav-pills nav-stacked menu_level_1">
{% for category in categories %}
<li class="nav-item{{ loop.first ? ' first' : loop.last ? ' last' : '' }}{{ currentCategory and (currentCategory.id == category.id) ? ' active' : '' }}">
<a href="{{ path('webburza_article_category_frontend_show', {slug: category.slug}) }}">
<i class="icon-angle-right"></i> <span>{{ category.title }}</span>
{% if categories is not empty %}
<ul class="nav">
<li class="first">
<span class="nav-header">
<span>{{ 'webburza.sylius.article_category.index_header' | trans }}</span>
</span>

<ul class="nav nav-pills nav-stacked menu_level_1">
{% for category in categories %}
<li class="nav-item{{ loop.first ? ' first' : loop.last ? ' last' : '' }}{{ currentCategory and (currentCategory.id == category.id) ? ' active' : '' }}">
<a href="{{ path('webburza_article_category_frontend_show', {slug: category.slug}) }}">
<i class="icon-angle-right"></i> <span>{{ category.title }}</span>
</a>
</li>
{% endfor %}
</ul>
</li>
</ul>
{% endif %}

{% if products is not empty %}
<span class="nav-header">
{{ 'webburza.sylius.article.related_products'|trans }}
</span>

{% for product in products %}
{% if product.enabled %}
<div>
<a href="{{ path(product) }}" class="btn btn-link clearfix">
<img class="thumbnail" src="{{ product.image ? product.image.path|imagine_filter('sylius_small') : 'http://placehold.it/120x90' }}" alt="{{ product.name }}" />
<div class="pull-left">{{ product.name }}</div>
</a>
</li>
{% endfor %}
</ul>
</li>
</ul>
</div>
{% endif %}
{% endfor %}
{% endif %}
</div>
{% endmacro %}
6 changes: 3 additions & 3 deletions Resources/views/Frontend/Article/show.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
</div>

<div class="row">
<div class="col-md-7{{ categories is empty ? ' col-md-offset-2' : '' }}">
<div class="col-md-7{{ categories is empty and article.products is empty ? ' col-md-offset-2' : '' }}">
{% if article.image %}
<img src="{{ article.image.path|imagine_filter('sylius_large') }}" class="img-responsive img-thumbnail">

Expand All @@ -29,8 +29,8 @@
{{ article.content|raw }}
</div>

{% if categories is not empty %}
{{ macro.articleSidebar(categories, article.category, true) }}
{% if categories is not empty or article.products is not empty %}
{{ macro.articleSidebar(categories, article.category, article.products, true) }}
{% endif %}
</div>

Expand Down

0 comments on commit 541e13b

Please sign in to comment.