Magento 2.1 Custom module relationship - magento

I developed few custom modules for my Magento 2.1 store for smart manegmant of content in some of the CMS pages.
I used this tutorial https://www.ashsmith.io/magento2/module-from-scratch-introduction/ and this example https://github.com/ashsmith/magento2-blog-module-tutorial in order to do it.
Now, I have on page with list of FAQ, but each FAQ is belongs to FAQ Category (Not the Catalog Category).
So there is two custom modules here (FAQ Category and FAQ Question).
The FAQ Category only have Title field.
The FAQ Question have Title field, Answer (text editor) field, and FAQ Question dropdown (select box with list of all availble FAQ categories)
I don't know how to achive this.
What is the right way to do it? Especially the admin part.

I assume you want to join fields. You cannot do this by using virtual type in di.xml, so you need to follow these steps and update your files
#File etc/di.xml
<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
<arguments>
<argument name="collections" xsi:type="array">
<item name="namespace_modulename_listing_data_source" xsi:type="string">Namespace\Modulename\Model\Resource\Modulename\Grid\Collection</item>
</argument>
</arguments>
</type>
<type name="Namespace\Modulename\Model\Resource\Modulename\Grid\Collection">
<arguments>
<argument name="mainTable" xsi:type="string">tablename</argument>
<argument name="eventPrefix" xsi:type="string">namespace_modulename_grid_collection</argument>
<argument name="eventObject" xsi:type="string">namespace_grid_collection</argument>
<argument name="resourceModel" xsi:type="string">Namespace\Modulename\Model\Resource\Modulename</argument>
</arguments>
</type>
In Your Resource Model File Model/Resource/Modulename/Collection.php
<?php
namespace Namespace\Modulename\Model\Resource\Modulename;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
class Collection extends AbstractCollection
{
/**
* Define model & resource model
*/
const YOUR_TABLE = 'tablename';
public function __construct(
\Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory,
\Psr\Log\LoggerInterface $logger,
\Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
\Magento\Framework\Event\ManagerInterface $eventManager,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Framework\DB\Adapter\AdapterInterface $connection = null,
\Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null
) {
$this->_init(
'Namespace\Modulename\Model\Modulename',
'Namespace\Modulename\Model\Resource\Modulename'
);
parent::__construct(
$entityFactory, $logger, $fetchStrategy, $eventManager, $connection,
$resource
);
$this->storeManager = $storeManager;
}
protected function _initSelect()
{
parent::_initSelect();
$this->getSelect()->joinLeft(
['join_table' => $this->getTable('tablename')],
'main_table.columnname = join_table.column_name',
'*'
);
}
}
?>
Now your Model/Resource/ModuleName/Grid/Collection.php
<?php
namespace Namespace\Modulename\Model\Resource\Modulename\Grid;
use Magento\Framework\Api\Search\SearchResultInterface;
use Magento\Framework\Search\AggregationInterface;
use Namespace\Modulename\Model\Resource\Modulename\Collection as ModulenameCollection;
/**
* Class Collection
* Collection for displaying grid
*/
class Collection extends ModulenameCollection implements SearchResultInterface
{
/**
* Resource initialization
* #return $this
*/
public function __construct(
\Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory,
\Psr\Log\LoggerInterface $logger,
\Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
\Magento\Framework\Event\ManagerInterface $eventManager,
\Magento\Store\Model\StoreManagerInterface $storeManager,
$mainTable,
$eventPrefix,
$eventObject,
$resourceModel,
$model = 'Magento\Framework\View\Element\UiComponent\DataProvider\Document',
$connection = null,
\Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null
) {
parent::__construct(
$entityFactory,
$logger,
$fetchStrategy,
$eventManager,
$storeManager,
$connection,
$resource
);
$this->_eventPrefix = $eventPrefix;
$this->_eventObject = $eventObject;
$this->_init($model, $resourceModel);
$this->setMainTable($mainTable);
}
/**
* #return AggregationInterface
*/
public function getAggregations()
{
return $this->aggregations;
}
/**
* #param AggregationInterface $aggregations
*
* #return $this
*/
public function setAggregations($aggregations)
{
$this->aggregations = $aggregations;
}
/**
* Get search criteria.
*
* #return \Magento\Framework\Api\SearchCriteriaInterface|null
*/
public function getSearchCriteria()
{
return null;
}
/**
* Set search criteria.
*
* #param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
*
* #return $this
* #SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function setSearchCriteria(
\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria = null
) {
return $this;
}
/**
* Get total count.
*
* #return int
*/
public function getTotalCount()
{
return $this->getSize();
}
/**
* Set total count.
*
* #param int $totalCount
*
* #return $this
* #SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function setTotalCount($totalCount)
{
return $this;
}
/**
* Set items list.
*
* #param \Magento\Framework\Api\ExtensibleDataInterface[] $items
*
* #return $this
* #SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function setItems(array $items = null)
{
return $this;
}
}
?>
Now you can use the join table columns any where in your grid and when you call collection.

Related

I want to combine two separate pagination models in laravel

I want to combine two separate pagination model in laravel, but errors in the view and paging does not work.
my controller code ,I also tried different methods, data comes but paging is not my goal paging
<?php
namespace App\Http\Controllers\Frontend\News;
use App\Http\Controllers\Controller;
use App\Models\Blog;
use App\Models\Company\CompanyEvent;
class NewsController extends Controller
{
function news()
{
$blogs = Blog::sort()->with('company')->paginate(15);
$events = CompanyEvent::sort()->with('company')->paginate(15);
$events=collect($events)->merge($blogs);
return view('news.news',compact('events'));
}
function detail()
{
return "detail";
}
}
blog model
<?php
namespace App\Models;
use App\Models\Company\Company;
use App\Models\Interfaces\CreatedAt;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Http\Request;
/**
* Class Blog
* #package App\Models
* #mixin Builder
* #property boolean $active
* #property int $company_id
* #property string $title
* #property string $url
* #property string $context
* #property string $tags
* #property string $main
* #property string $seo_title
* #property string seo_description
* #property string $seo_keyword
* #property string $image
* #property-read CreatedAt|Carbon $created_at
* #property-read CreatedAt|Carbon $updated_at
* #property-read CreatedAt|Carbon $deleted_at
* #method static Builder|Blog find(int $id)
* #method static Builder|Blog where($column, $operator = null, $value = null, $boolean = 'and')
* #method static Builder|Blog findOrFail($id)
* #method static Builder|Blog active()
* #method Builder|Blog filter(Request $request) bind to scopeFilter method
* #method static Builder|Blog sort() laravel query bind to scopeSort method
* #method static Builder|Blog latest() laravel query bind to scopeSort method
*
*/
class Blog extends Model
{
use SoftDeletes;
protected $table = 'blogs';
/**
* #return bool
*/
public function isActive(): bool
{
return (bool)$this->active;
}
/**
* #param bool $save
* #return bool
*/
public function toggleActive($save = false)
{
$this->active = !$this->active;
if ($save == true)
return $this->save();
return false;
}
/**
* #return MorphToMany
*/
function comments()
{
return $this->morphToMany('App\Models\Blog', 'commentable');
}
/**
* #return string
*/
function getAgoTimeAttribute()
{
return Carbon::parse($this->created_at)->diffForHumans();
}
/**
* #param $value
* #return array
*
*/
function getTagsAsArrayAttribute($value)
{
return explode(',', $this->tags);
}
/**
* #param Builder|Blog $query
* #return mixed
*
*/
function scopeActive($query)
{
return $query->where('active', 1);
}
/**
* #return BelongsTo
*/
function company()
{
return $this->belongsTo(Company::class, 'company_id', 'id');
}
/**
* #return string
*/
function getTimeAgoAttribute()
{
return Carbon::parse($this->created_at)->diffForHumans();
}
/**
* #return false|string
*/
function getShortContextAttribute()
{
return substr($this->context, 0, 250);
}
/**
* #param Blog|Builder $query
* #param Request $request
* #return mixed
*/
function scopeFilter($query, $request)
{
$requests = $request->except('page');
foreach ($requests as $key => $val)
$query = $query->where($key, 'like', '%' . $val . '%');
return $query;
}
/**
* #param Builder|Blog $query
* #return mixed
*/
function scopeSort($query)
{
return $query->orderBy('created_at', 'desc');
}
}
company event model
<?php
namespace App\Models\Company;
use App\Calendar\Day\Day;
use App\Calendar\Month\Month;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Query\Builder;
/**
* Class CompanyEvent
* #mixin Builder
* #package App\Models\Company
* #property \DateTime $date
* #method static Builder|CompanyEvent get()
* #method static Builder|CompanyEvent find($id)
* #method static Builder|CompanyEvent findOrFail($id)
* #method static Builder|CompanyEvent active() bind to scopeActive
* #method static Builder|CompanyEvent sort()
* #method static Builder|CompanyEvent latest()
*
*/
class CompanyEvent extends Model
{
use SoftDeletes;
protected $table = 'company_events';
/**
* get day name from date column
* #return string
*
*/
function getDayNameAttribute()
{
$dayIndex = (int)date('w', strtotime($this->date));
return ucfirst(Day::DAY_NAMES[$dayIndex]);
}
/**
* get month from date column
* #return string
*/
function getMonthAttribute()
{
$monthIndex = (int)date('m', strtotime($this->date));
return ucfirst(Month::MONTH_NAMES[$monthIndex - 1]);
}
/**
* #return false|string
*/
function getDayAttribute()
{
return date('d', strtotime($this->date));
}
/**
* #return bool
*
*/
function getIsExpireDateAttribute()
{
$date = date('Y-m-d', strtotime($this->date));
$todayDateAsTime = strtotime(date('Y-m-d'));
$eventDateAsTime = strtotime($date);
if ($todayDateAsTime > $eventDateAsTime)
return true;
return false;
}
/**
* #return bool
*
*/
function getIsEventTodayAttribute()
{
$date = date('Y-m-d', strtotime($this->date));
$todayDateAsTime = strtotime(date('Y-m-d'));
$eventDateAsTime = strtotime($date);
return (boolean)($eventDateAsTime == $todayDateAsTime);
}
/**
* #return string
*/
function getAgoTimeAttribute()
{
return Carbon::parse($this->date)->diffForHumans();
}
/**
* #return BelongsTo
*/
function company()
{
return $this->belongsTo(Company::class, 'company_id', 'id');
}
/**
* #param Builder|CompanyEvent $query
* #return mixed
*/
function scopeActive($query)
{
return $query->where('active', 1);
}
/**
* #param Builder|CompanyEvent $query
* #return mixed
*/
function scopeSort($query)
{
return $query->orderBy('created_at','desc');
}
}
I also tried different methods, data comes but paging is not my goal paging
my view code
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Mr. Burhan</title>
</head>
<body>
{{$events->total()}} adet bulundu
<ul>
#foreach( $events as $value )
<li>{{$value}}</li>
#endforeach
</ul>
{{$events->appends(request()->all())->render()}}
</body>
</html>

Store JSON data into TEXT mysql column with doctrine

I have an entity with one TEXT (MySQL) attributes
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\Table;
use Doctrine\ORM\Mapping\Index;
use ApiPlatform\Core\Annotation\ApiProperty;
/**
* #ApiResource(
* attributes={},
* collectionOperations={
* "get"={},
* "post"={
* "access_control"="is_granted('ROLE_COMPANY')"
* },
* },
* itemOperations={
* "get"={},
* "put"={"access_control"="is_granted('ROLE_COMPANY')"},
* }
* )
* #ORM\Entity(
* repositoryClass="App\Repository\SettingRepository",
* )
* #ORM\Table(
* indexes={#Index(name="domain_idx", columns={"domain"})}
* )
*/
class Setting
{
/**
* #var Uuid
* #ApiProperty(identifier=true)
* #ORM\Id
* #ORM\Column(type="string")
* #ORM\GeneratedValue(strategy="NONE")
*/
private $identifier;
/**
* #ORM\Column(type="text", nullable=true)
*/
private $data = array();
/**
* #ORM\Column(type="string", nullable=true)
*/
private $domain = array();
public function getData()
{
if($this->data == null) return array();
$data = unserialize($this->data);
return $data;
}
public function setData($data): self
{
$this->data = serialize($data);
return $this;
}
/**
* #return mixed
*/
public function getIdentifier()
{
return $this->identifier;
}
/**
* #param mixed $key
*/
public function setIdentifier($identifier): self
{
$this->identifier = $identifier;
return $this;
}
/**
* #return mixed
*/
public function getDomain()
{
return $this->domain;
}
/**
* #param mixed $domain
*/
public function setDomain($domain): self
{
$this->domain = $domain;
return $this;
}
}
If I try to invoke the service with the following parameter structure it works fine:
{
"data": "testData",
"identifier": "testIdentifier",
"domain": "domain1"
}
But If I would like to store an embedded JSON string, for example:
"data": {"temp": 123}
I receive the following error:
hydra:description": "The type of the \"data\" attribute must be \"string\", \"array\" given.",
I tried to convert the object into an string in the method setData. But this method will not be invoked. It seams, that the API-Platform detects the wrong type and throws the exception.
I found some comments, that it is necessary to decorate the property:
https://api-platform.com/docs/core/serialization/#decorating-a-serializer-and-adding-extra-data
Can anyone give me an example? It does not work!
Where is the right place to serialise and unserialise the property data?
Does anyone have an idea?
Kind regards
You need to set the column type to json in MySQL. It should behave as expected.
/**
* #var array Additional data describing the setting.
* #ORM\Column(type="json", nullable=true)
*/
private $data = null;
I think null is more consistent than an empty array, but that's your choice.

CollectionType with multiple entity type + Ajax

I have a problem creating a form by including a formType that are 2 related entities and that must be combined with a CollectionType.
I use the library jquery.collection for CollectionType ( http://symfony-collection.fuz.org/symfony3/ )
My entities
Product
namespace BBW\ProductBundle\Entity\Product;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* Product
*
* #ORM\Table(name="product_product")
* #ORM\Entity(repositoryClass="BBW\ProductBundle\Repository\Product\ProductRepository")
*/
class Product
{
use ORMBehaviors\Translatable\Translatable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Attribute\AttributeGroup", inversedBy="products", fetch="EAGER")
* #ORM\JoinTable(name="product_join_attribute_group")
*/
private $attributeGroups;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Attribute\Attribute", inversedBy="products", fetch="EAGER")
* #ORM\JoinTable(name="product_join_attribute")
*/
private $attributes;
/**
* Constructor
*/
public function __construct()
{
$this->attributeGroups = new \Doctrine\Common\Collections\ArrayCollection();
$this->attributes = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add attributeGroup
*
* #param \BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup
*
* #return Product
*/
public function addAttributeGroup(\BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup)
{
$this->attributeGroups[] = $attributeGroup;
return $this;
}
/**
* Remove attributeGroup
*
* #param \BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup
*/
public function removeAttributeGroup(\BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup)
{
$this->attributeGroups->removeElement($attributeGroup);
}
/**
* Get attributeGroups
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAttributeGroups()
{
return $this->attributeGroups;
}
/**
* Add attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*
* #return Product
*/
public function addAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes[] = $attribute;
return $this;
}
/**
* Remove attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*/
public function removeAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes->removeElement($attribute);
}
/**
* Get attributes
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAttributes()
{
return $this->attributes;
}
}
AttributeGroup
namespace BBW\ProductBundle\Entity\Attribute;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* AttributeGroup
*
* #ORM\Table(name="product_attribute_group")
* #ORM\Entity(repositoryClass="BBW\ProductBundle\Repository\Attribute\AttributeGroupRepository")
*/
class AttributeGroup
{
use ORMBehaviors\Translatable\Translatable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\OneToMany(targetEntity="BBW\ProductBundle\Entity\Attribute\Attribute", mappedBy="attributeGroup", cascade={"persist", "remove"}, fetch="EAGER")
* #ORM\JoinColumn(name="attribute_id")
*/
private $attributes ;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Product\Product", mappedBy="attributeGroups", cascade={"persist"}, fetch="EAGER")
*/
private $products;
/**
* Constructor
*/
public function __construct()
{
$this->attributes = new \Doctrine\Common\Collections\ArrayCollection();
$this->products = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*
* #return AttributeGroup
*/
public function addAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes[] = $attribute;
return $this;
}
/**
* Remove attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*/
public function removeAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes->removeElement($attribute);
}
/**
* Get attributes
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* Add product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*
* #return AttributeGroup
*/
public function addProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products[] = $product;
return $this;
}
/**
* Remove product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*/
public function removeProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products->removeElement($product);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
}
Attributes
namespace BBW\ProductBundle\Entity\Attribute;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* Attribute
*
* #ORM\Table(name="product_attribute")
* #ORM\Entity(repositoryClass="BBW\ProductBundle\Repository\Attribute\AttributeRepository")
*/
class Attribute
{
use ORMBehaviors\Translatable\Translatable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\ManyToOne(targetEntity="BBW\ProductBundle\Entity\Attribute\AttributeGroup", inversedBy="attributes", cascade={"persist"}, fetch="EAGER")
* #ORM\JoinColumn(name="attribute_group_id")
*/
private $attributeGroup ;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Product\Product", mappedBy="attributes", cascade={"persist"}, fetch="EAGER")
*/
private $products;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set attributeGroup
*
* #param \BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup
*
* #return Attribute
*/
public function setAttributeGroup(\BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup = null)
{
$this->attributeGroup = $attributeGroup;
return $this;
}
/**
* Get attributeGroup
*
* #return \BBW\ProductBundle\Entity\Attribute\AttributeGroup
*/
public function getAttributeGroup()
{
return $this->attributeGroup;
}
/**
* Constructor
*/
public function __construct()
{
$this->products = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*
* #return Attribute
*/
public function addProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products[] = $product;
return $this;
}
/**
* Remove product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*/
public function removeProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products->removeElement($product);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
}
Explanation
My product must belong to an attribute group and must contain attributes. I have a relationship between my AttributeGroup entity and a Relationship with Attribute.
My Entity AttributeGroup has a relationship with Attribute (functional).
Form Types
In my form I have a CollectionType with another formType as entry_type I have created with two entity types (AttributeGroups / Attributes)
FormType for product
namespace BBW\ProductBundle\Form\Product;
use A2lix\TranslationFormBundle\Form\Type\TranslationsType;
use BBW\CoreBundle\FormTypes\SwitchType;
use BBW\MediaBundle\Transformers\MediaTransformer;
use BBW\ProductBundle\Form\Attribute\Type\AttributeType;
use Doctrine\ORM\Mapping\Entity;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ProductEditType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$locale = $options['int_service'];
$builder
->add('attributes', CollectionType::class, array(
'entry_type' => AttributeType::class,
'entry_options' => array(
'label' => false
),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'attr' => array(
'class' => 'attributes-selector'
),
))
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'BBW\ProductBundle\Entity\Product\Product',
'int_service' => ['fr'],
'translation_domain' => 'ProductBundle'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'bbw_productbundle_edit_product';
}
}
AttributeType
/**
* Form Type: Fields add in product edit form
*/
namespace BBW\ProductBundle\Form\Attribute\Type;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class AttributeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('attributeGroups', EntityType::class, array(
'label' => false,
'class' => 'BBW\ProductBundle\Entity\Attribute\AttributeGroup',
'choice_label' => 'translate.name',
'attr' => array(
'class' => 'choiceAttributeGroup'
),
))
->add('attributes', EntityType::class, array(
'label' => false,
'class' => 'BBW\ProductBundle\Entity\Attribute\Attribute',
'choice_label' => 'translate.name',
'attr' => array(
'class' => 'choiceAttributes'
)
))
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'BBW\ProductBundle\Entity\Product\Product',
'int_service' => ['fr'],
'translation_domain' => 'ProductBundle'
));
}
}
Explanation
The two entities (AttributeGroups / Attributes) are two linked drop-down lists. When I select a group, I must display the data of this group in the second drop-down list. This part works well. I can duplicate as much as I want (with the CollectionType).
First problem when i submit the form:
"Could not determine access type for property "attributeGroups" ".
Second problem when i set "mapped => false" in my two entities:
"Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "BBW\ProductBundle\Entity\Product\Product#$attributes", got "BBW\ProductBundle\Entity\Product\Product" instead."
Screeshoots dump/form
Conclusion
I think my problem is a mapping problem, having tested a lot of code and explanation on the symfony doc or other site, I could not solve my problem. If anyone could help me out and explain the good work of a collectionType with 2 entity and if that is possible. If you have examples of codes I am taker too.
Thank you in advance for your help.

Magento 2 - Set product attribute to use default values

I want to check Use as Default for all product for a particular store view
I use this code
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$collection = $this->_productCollectionFactory->create();
foreach($collection as $data){
$product=$this->_product->setStoreId(1)->load($data->getEntityId());
$product->setData('name',false);
$product->save();
}
But this is removing product from categories.
Can you please let me know how I can check Use as default checkbox programmatically.
Magento 2.1.3 EE
The following solution creates a CLI command to directly manipulate the database and delete store specific product attribute information. It was written for Magento Enterprise edition, so if you're using Community Edition you'll have to modify this code to utilize entity_id instead of row_id.
Please be careful with this. The solution laid forth here bypasses model classes and performs direct delete queries on the default database connection's catalog_product_entity_datetime, catalog_product_entity_decimal, catalog_product_entity_int, catalog_product_entity_text, and catalog_product_entity_varchar tables. Back up your database first.
Step 1: Create the module
app/code/StackOverflow/Question40177336/registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'StackOverflow_Question40177336',
__DIR__
);
app/code/StackOverflow/Question40177336/etc/module.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="StackOverflow_Question40177336" setup_version="0.0.1"/>
</config>
app/code/StackOverflow/Question40177336/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Framework\Console\CommandListInterface">
<arguments>
<argument name="commands" xsi:type="array">
<item name="stackoverflow_question40177336" xsi:type="object">StackOverflow\Question40177336\Console\Command\Product\UseDefaultValue</item>
</argument>
</arguments>
</type>
</config>
app/code/StackOverflow/Question40177336/Console/Command/Product/UseDefaultValue.php
<?php
namespace StackOverflow\Question40177336\Console\Command\Product;
use Magento\Catalog\Model\Product;
use Magento\Eav\Setup\EavSetup;
use Magento\Framework\App\ResourceConnection;
use Magento\Store\Model\StoreManagerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class UseDefaultValue extends Command
{
/**
* flag indicating if the command has been initialized yet
*
* #var bool
*/
protected $initialized = false;
/**
* The attribute_id to use for the current command.
*
* #var int
*/
protected $attributeId;
/**
* The row_id values(s) to use for the command (if any).
*
* #var array|bool
*/
protected $rowIds;
/**
* The store_id to use for the current command.
*
* #var int
*/
protected $storeId;
/**
* The table name to use for the current command.
*
* #var string
*/
protected $tableName;
/**
* #var \Magento\Framework\DB\Adapter\AdapterInterface
*/
protected $connection;
/**
* #var EavSetup
*/
protected $eavSetup;
/**
* #var StoreManagerInterface
*/
protected $storeManager;
public function __construct(
EavSetup $eavSetup,
ResourceConnection $resourceConnection,
StoreManagerInterface $storeManager
) {
$this->connection = $resourceConnection->getConnection();
$this->eavSetup = $eavSetup;
$this->storeManager = $storeManager;
parent::__construct();
}
/**
* Configures the current command.
*/
protected function configure()
{
$this
->setName('catalog:product:attributes:use-default-value')
->setDescription('Removes store specific data from a product(s) of given attribute code.')
->addArgument(
'attribute_code',
InputArgument::REQUIRED,
'Attribute Code'
)
->addArgument(
'store',
InputArgument::REQUIRED,
'Store code or store_id (cannot be \'admin\' or \'0\')'
)
->addArgument(
'sku',
InputArgument::OPTIONAL,
'Sku (omit to apply to all products)'
)
;
}
/**
* Executes the current command.
*
* #param InputInterface $input An InputInterface instance
* #param OutputInterface $output An OutputInterface instance
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->init($input);
$conn = $this->connection;
$bind = [
$conn->quoteInto('store_id = ?', $this->getStoreId()),
$conn->quoteInto('attribute_id = ?', $this->getAttributeId())
];
if ($this->getRowIds()) {
$bind[] = $conn->quoteInto('row_id IN (?)', $this->getRowIds());
}
$rows = $conn->delete($this->getTableName(), $bind);
$output->writeln($rows.' rows deleted.');
}
/**
* Return the row_id value(s) to use for the command (if any).
*
* #return array|boolean
*/
protected function getRowIds()
{
if (!$this->initialized) {
$this->errorInit(__METHOD__);
}
return $this->rowIds;
}
/**
* Initializes some class properties.
*
* #param InputInterface $input
*/
protected function init(InputInterface $input)
{
if (!$this->initialized) {
$attributeCode = trim($input->getArgument('attribute_code'));
if ($attributeCode == '') {
throw new \RuntimeException(__('attribute_code is required.'));
} elseif (is_numeric($attributeCode)) {
throw new \RuntimeException(__('attribute_code cannot be numeric.'));
}
$attribute = $this->eavSetup->getAttribute(
Product::ENTITY,
$attributeCode
);
if (!$attribute) {
throw new \RuntimeException(__('Invalid attribute_code "%1"', $attributeCode));
}
$backendType = $attribute['backend_type'];
$allowedTypes = ['datetime','decimal','int','text','varchar'];
if (!in_array($backendType, $allowedTypes)) {
throw new \RuntimeException(__(
'backend_type "%1" is not allowed. Allowed types include: %2',
$backendType,
implode(', ', $allowedTypes)
));
}
$this->tableName = $this->connection->getTableName('catalog_product_entity_'.$backendType);
$this->attributeId = (int) $attribute['attribute_id'];
$store = $this->storeManager->getStore($input->getArgument('store'));
if ($store->getCode() == 'admin') {
throw new \RuntimeException(__('Admin Store is not allowed for this command.'));
}
$this->storeId = (int) $store->getId();
$sku = trim($input->getArgument('sku'));
if ($sku != '') {
$sql = $this->connection->select()
->from($this->connection->getTableName('catalog_product_entity'), 'row_id')
->where('sku = ?', $sku)
;
$rowIds = $this->connection->fetchCol($sql);
if (!$rowIds) {
throw new \RuntimeException(__('Invalid Sku "%1"', $sku));
}
foreach ($rowIds as $k => $v) {
$rowIds[$k] = (int) $v;
}
$this->rowIds = $rowIds;
} else {
$this->rowIds = false;
}
$this->initialized = true;
}
}
/**
* Returns the attribute_id to use for the current command.
*
* #return int
*/
protected function getAttributeId()
{
if (!$this->attributeId) {
$this->errorInit(__METHOD__);
}
return $this->attributeId;
}
/**
* Return the store id to use for the current command.
*
* #param InputInterface $input
*/
protected function getStoreId()
{
if (!$this->storeId) {
$this->errorInit(__METHOD__);
}
return $this->storeId;
}
/**
* Return the qualified table name to use for the current command.
*
* #param InputInterface $input
*/
protected function getTableName()
{
if (!$this->tableName) {
$this->errorInit(__METHOD__);
}
return $this->tableName;
}
/**
* Throws an exception.
*
* #param string $methodName
* #throws \LogicException
*/
protected function errorInit($methodName)
{
throw new \LogicException(
__('Command has not been intialized. Call UseDefaultValue::init() before calling '.$methodName));
;
}
}
Step 2: Enable the module
php -f bin/magento module:enable StackOverflow_Question40177336
Step 3: Utilize your new CLI command.
The command has two required arguments, attribute_code and store. Store can be either the ID or Code. Admin store is not allowed for obvious reasons. The command also has an optional third parameter of SKU if you wish to only target a specific SKU (omitting this applies to all products).
For example, if you wanted to delete all "name" values from the "default" store view your command would be as follows:
php -f bin/magento catalog:product:attributes:use-default-value name default

Magento 2 - Set collection limit in a Model

I created a model file which I would like a method inside me to return products with a limit.
Below my model:
<?php
namespace Test\Ice\Model;
class Data extends \Magento\Framework\App\Helper\AbstractHelper {
/**
* System configuration values
*
* #var array
*/
protected $_config;
/**
* Store manager interface
*
*/
protected $_storeManager;
/**
* Product interface
*
*/
protected $_product;
/**
* Initialize
*
* #param Magento\Framework\App\Helper\Context $context
* #param Magento\Catalog\Model\ProductFactory $productFactory
* #param Magento\Store\Model\StoreManagerInterface $storeManager
* #param Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* #param array $data
*/
public function __construct(
\Magento\Framework\App\Helper\Context $context,
\Magento\Catalog\Model\ResourceModel\Product\Collection $collection,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
array $data = []
) {
$this->_product = $collection;
$this->_storeManager = $storeManager;
parent::__construct($context, $data);
}
public function getProducts() {
$limit = 10;
return $this->_product->getSelect()->limit($limit);
}
}
The problem is that it does not make the limit, but always returns all products in the collection.
Where am I wrong?
Thank you
Use
$this->_product->getCollection()->setPageSize(10)->setCurPag‌​e(1);

Resources