Update field of database - magento

I have this code to udpdate max length of an attribute
class UpgradeData implements UpgradeDataInterface
{
public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
{
$setup->startSetup();
if ($context->getVersion()
&& version_compare($context->getVersion(), '1.0.2') < 0
) {
$table = $setup->getTable('eav_attribute');
$setup->getConnection()
->update($table, ['frontend_class' => 'validate-length maximum-length-70'], 'attribute_id = 73');
}
$setup->endSetup();
}
}
It works fine bu instad of attribute_id = 73 I want to put attribute_code= name but it does not work.

Try this
class UpgradeData implements UpgradeDataInterface
{
/**
* #var \Magento\Eav\Setup\EavSetupFactory
*/
private $eavSetupFactory;
public function __construct(\Magento\Eav\Setup\EavSetupFactory $eavSetupFactory)
{
$this->eavSetupFactory = $eavSetupFactory;
}
public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
{
$setup->startSetup();
if ($context->getVersion()
&& version_compare($context->getVersion(), '1.0.2') < 0
) {
$eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
$this->eavSetup->updateAttribute(
'catalog_product',
'ATTRIBUTE_CODE_GOES_HERE',
'frontend_class',
'validate-length maximum-length-70'
);
}
$setup->endSetup();
}
}
Watch out for typos. I didn't check the code.

Related

Files collection with Api-Platform and VichUploadBundle

I am using Api-Platform in latest version and I would like my user to be able to attach multiple files when writing a note.
As a first step, I'm trying to make this work with Postman. I chose VichUploadBundle as the documentation says.
Only I'm blocked for publishing a note with attachments. I think I did it right overall, but I have to intervene again on one point, I can't find the subtlety.
I specify that I am able to create a simple file, without relation. Thanks to the custom controller. But for the relationship, I obviously need the MultipartDecoder.
The File Object :
#[Vich\Uploadable]
#[ORM\Entity]
#[ApiResource(
types: ['https://schema.org/MediaObject'],
operations: [
new Get(),
new GetCollection(),
new Post(
controller: CreateMediaObjectAction::class,
openapiContext: [
'requestBody' => [
'content' => [
'multipart/form-data' => [
'schema' => [
'type' => 'object',
'properties' => [
'file' => [
'type' => 'string',
'format' => 'binary'
]
]
]
]
]
]
],
deserialize: false
)
],
normalizationContext: ['groups' => ['media_object:get']]
)]
class MediaObject
{
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
private ?int $id = null;
#[ApiProperty(types: ['https://schema.org/contentUrl'])]
#[Groups([
'media_object:get'
])]
public ?string $contentUrl = null;
#[Vich\UploadableField(mapping: "media_object", fileNameProperty: "filePath")]
public ?File $file = null;
#[ORM\Column(nullable: true)]
public ?string $filePath = null;
#[ORM\ManyToOne(inversedBy: 'files')]
#[ORM\JoinColumn(nullable: false)]
private Note $note;
public function getId(): ?int
{
return $this->id;
}
/**
* #return Note
*/
public function getNote(): Note
{
return $this->note;
}
/**
* #param Note $note
*/
public function setNote(Note $note): void
{
$this->note = $note;
}
}
Note Object :
#[ORM\Entity(repositoryClass: NoteRepository::class)]
#[ApiResource(
operations: [
new GetCollection(
normalizationContext: ['groups' => ['note:get:collection']],
security: "is_granted('ROLE_ADMIN') or is_granted('ROLE_SIEGE')"
),
new Post(
inputFormats: ['multipart' => ['multipart/form-data']],
denormalizationContext: ['groups' => ['note:post:item']],
securityPostDenormalize: "is_granted('POST_NOTE', object)"
),
new Get(
security: "is_granted('GET_NOTE', object)"
),
new Put(
denormalizationContext: ['groups' => ['note:post:item']],
security: "is_granted('PUT_NOTE', object)"
),
new Delete(
security: "is_granted('DELETE_NOTE', object)"
)
],
normalizationContext: ['groups' => ['note:get:item']],
paginationClientItemsPerPage: true,
)]
class Note
{
...
#[Groups([
'note:get:item',
'note:post:item'
])]
#[ORM\OneToMany(mappedBy: 'note', targetEntity: MediaObject::class)]
public Collection $files;
public function __construct()
{
$this->files = new ArrayCollection();
}
...
}
MultipartDecoder :
final class MultipartDecoder implements DecoderInterface
{
public const FORMAT = 'multipart';
public function __construct(private RequestStack $requestStack) {}
/**
* {#inheritdoc}
*/
public function decode(string $data, string $format, array $context = []): ?array
{
$request = $this->requestStack->getCurrentRequest();
if (!$request) {
return null;
}
return array_map(static function (string $element) {
// Multipart form values will be encoded in JSON.
$decoded = json_decode($element, true);
return \is_array($decoded) ? $decoded : $element;
}, $request->request->all()) + $request->files->all();
}
/**
* {#inheritdoc}
*/
public function supportsDecoding(string $format): bool
{
return self::FORMAT === $format;
}
}
Denormalizer :
final class UploadedFileDenormalizer implements DenormalizerInterface
{
/**
* {#inheritdoc}
*/
public function denormalize($data, string $type, string $format = null, array $context = []): UploadedFile
{
return $data;
}
/**
* {#inheritdoc}
*/
public function supportsDenormalization($data, $type, $format = null): bool
{
return $data instanceof UploadedFile;
}
}
Normalizer :
final class MediaObjectNormalizer implements NormalizerInterface, NormalizerAwareInterface
{
use NormalizerAwareTrait;
private const ALREADY_CALLED = 'MEDIA_OBJECT_NORMALIZER_ALREADY_CALLED';
public function __construct(private StorageInterface $storage)
{
}
public function normalize($object, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
{
$context[self::ALREADY_CALLED] = true;
$object->contentUrl = $this->storage->resolveUri($object, 'file');
return $this->normalizer->normalize($object, $format, $context);
}
public function supportsNormalization($data, ?string $format = null, array $context = []): bool
{
if (isset($context[self::ALREADY_CALLED])) {
return false;
}
return $data instanceof MediaObject;
}
}
The message changes depending on whether or not there are brackets on the key files
files[] :
"Expected IRI or nested document for attribute "files", "object" given."

Call to a member function dispatch() on null in vendor/magento/framework/Model/AbstractModel.php

I want to insert some data for the first time the module is being installed.
This is my folder structure
Navien/Custom
-- Setup/InstallData.php
-- Model/
-- StateData.php
-- StateMaster.php
-- ResourceModel/
-- StateMaster.php
-- StateMaster/
-- Collection.php
Here is content of my script InstallData.php
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Eav\Model\Config;
use Navien\Custom\Model\StateData;
class InstallData implements InstallDataInterface
{
private $eavSetupFactory;
private $eavConfig;
private $stateDataMaster;
public function __construct(EavSetupFactory $eavSetupFactory, Config $eavConfig, StateData $stateData)
{
$this->eavSetupFactory = $eavSetupFactory;
$this->eavConfig = $eavConfig;
$this->stateDataMaster = $stateData;
}
public function install(
ModuleDataSetupInterface $setup,
ModuleContextInterface $context
)
{
$data = [
['code' => 'AL', 'name' => 'Alabama', 'abbreviation' => 'Ala.', 'country' => 'US'],
['code' => 'AK', 'name' => 'Alaska', 'abbreviation' => 'Alaska', 'country' => 'US']
];
$this->stateDataMaster->insertStates($data);
}
}
Model/StateData.php
namespace Navien\Custom\Model;
use Navien\Custom\Model\ResourceModel\StateMaster;
use Psr\Log\LoggerInterface;
use Magento\Framework\View\Element\Template\Context;
// class StateData extends \Magento\Framework\View\Element\Template
class StateData
{
protected $_logger;
protected $_stateMaster;
protected $_objectManager;
public function __construct(
LoggerInterface $logger,
StateMaster $stateMaster,
Context $context,
array $data = []
)
{
// parent::__construct($context, $data);
$this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$this->_logger = $logger;
}
public function insertStates($states)
{
if( !empty( $states ) )
{
$stateMasterRepository = $this->_objectManager->get('Navien\Custom\Model\StateMaster');
$stateData = $stateMasterRepository->getCollection()->getData();
foreach( $states as $key => $state )
{
if( (array_search($state['code'], array_column($stateData, 'code')) === FALSE) )
{
$stateMaster = $this->_objectManager->get('Navien\Custom\Model\StateMaster');
$stateMaster->setData( $state );
$stateMaster->save();
}
}
}
}
public function execute()
{
}
}
Model/StateMaster.php
namespace Navien\Custom\Model;
use \Magento\Framework\Model\AbstractModel;
use \Psr\Log\LoggerInterface;
class StateMaster extends AbstractModel
{
protected $_logger;
public function __construct(
LoggerInterface $logger
)
{
$this->_init('Navien\Custom\Model\ResourceModel\StateMaster');
}
}
Model/ResourceModel/StateMaster.php
namespace Navien\Custom\Model\ResourceModel;
use \Psr\Log\LoggerInterface;
class StateMaster extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
protected function _construct()
{
$this->_init('navien_states','ID');
}
}
Navien\Custom\Model\ResourceModel\StateMaster
namespace Navien\Custom\Model\ResourceModel\StateMaster;
class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
public function __construct(
\Magento\Framework\Data\Collection\EntityFactory $entityFactory,
\Psr\Log\LoggerInterface $logger,
\Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
\Magento\Framework\Event\ManagerInterface $eventManager,
\Magento\Catalog\Model\ResourceModel\Product\Option\Value\CollectionFactory $optionValueCollectionFactory,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Framework\DB\Adapter\AdapterInterface $connection = null,
\Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null
) {
$this->_optionValueCollectionFactory = $optionValueCollectionFactory;
$this->_storeManager = $storeManager;
parent::__construct(
$entityFactory,
$logger,
$fetchStrategy,
$eventManager,
$connection,
$resource
);
$this->logger = $logger;
}
protected function _construct()
{
$this->_init('Navien\Custom\Model\StateMaster','Navien\Custom\Model\ResourceModel\StateMaster');
}
}
Now I am getting this error
Error: Call to a member function dispatch() on null in /var/www/dev/vendor/magento/framework/Model/AbstractModel.php:701
Stack trace: #0 /var/www/dev/vendor/magento/framework/Model/ResourceModel/Db/AbstractDb.php(412): Magento\Framework\Model\AbstractModel->beforeSave()
#1 /var/www/dev/vendor/magento/framework/Model/AbstractModel.php(655): Magento\Framework\Model\ResourceModel\Db\AbstractDb->save()
#2 /var/www/dev/app/code/Navien/Custom/Model/StateData.php(62): Magento\Framework\Model\AbstractModel->save()
It's because your constructor does not match the parent block constructor.
Model/StateMaster.php
namespace Navien\Custom\Model;
use \Magento\Framework\Model\AbstractModel;
class StateMaster extends AbstractModel
{
public function __construct(
\Magento\Framework\Model\Context $context,
\Magento\Framework\Registry $registry,
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
array $data = []
) {
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
}
protected function _construct()
{
$this->_init(\Navien\Custom\Model\ResourceModel\StateMaster::class);
}
}

Laravel: ID as ValueObject is null after save

I'm using ValueObject casting as an ID of my model. Everything works fine when I get a record from database, however when it coming to saving, the ID is null. If I comment "casts" out, ID is correct.
Example:
$game = new Game($data);
$game->created_by = $userId; // Id ValueObject
$game->save();
dd($game);
// attributes:
// "id" => null,
// "created_by" => Id{#value: 10},
Id ValueObject:
class Id
{
public function get($model, $key, $value, $attributes)
{
$this->value = $value;
return $this;
}
public function set($model, $key, $value, $attributes)
{
$this->value = $value;
}
public function value(): int
{
return $this->value;
}
public function __toString()
{
return (string) $this->value;
}
}
Model:
class Game extends Model
{
protected $casts = [
'id' => Id::class
];
}
What can I do with it?
Thanks in advance
Okey, I think that there should be a ValueObject and a CastingObject, I ended up with something similar to this:
class Game extends Model
{
protected $casts = [
'id' => IdCast::class
];
}
class IdCast implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
{
return new Id(
$attributes['id']
);
}
public function set($model, $key, $value, $attributes)
{
return [
'id' => $value
];
}
}
class Id
{
private $value;
public function __construct($id)
{
$this->value = $id;
}
public function value(): int
{
return $this->value;
}
public function __toString()
{
return (string) $this->value;
}
}

Return collection with custom resolver/data provider on ApiPlatform with GraphQL

I work with PHP - Symfony 5.
I would like to return a collection with graphQL on Api Platform but I have a problem :
"debugMessage": "Service \"App\\DataProvider\\CompaniesCollectionDataProvider\" not found: the container inside \"Symfony\\Component\\DependencyInjection\\Argument\\ServiceLocator\" is a smaller service locator that only knows about the \"App\\Resolver\\CheckRecruiterQueryResolver\", \"App\\Resolver\\ForgotRecruiterQueryResolver\" and \"App\\Resolver\\JobBoard\\FindOffersByCompanyQueryResolver\" services.",
First I had created a resolver but looking at the documentation, I realized that it was necessary to make a data provider.
Unfortunately that did not solve the problem, I still have the same error message...
My custom data Provider :
namespace App\DataProvider;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use App\Entity\Candidate;
use App\Entity\Company;
use Doctrine\Persistence\ManagerRegistry;
class CompaniesCollectionDataProvider implements CollectionDataProviderInterface
{
private $doctrine;
public function __construct(ManagerRegistry $doctrine)
{
$this->doctrine = $doctrine;
}
public function getCollection
(string $resourceClass, string $operationName = null, array $context = [])
{
$candidateId = $context['filters']['candidate_id'];
$candidate = $this->doctrine->getRepository(Candidate::class)
->findOneBy(['id' => $candidateId]);
if(empty($candidate->getPosition()))
{
$companies = $this->doctrine->getRepository(Company::class)
->findByStoreIsNational();
// return companies where stores related = is_national
// getCollection doit retourner un array
return $companies;
}
$companies = $this->doctrine->getRepository(Company::class)
->findByStoreIsNational();
return $companies;
}
}
My entity :
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\DataProvider\CompaniesCollectionDataProvider;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ApiResource(graphql={
* "search"={
* "collection_query"=CompaniesCollectionDataProvider::class,
* "args"={
* "candidate_id"={"type"="Int!", "description"="Candidate_id"}
* }
* },
* "create", "update", "delete", "collection_query", "item_query"
* })
*
* #Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false)
* #ORM\Entity(repositoryClass="App\Repository\CompanyRepository")
* #ORM\Table(name="companies")
* #UniqueEntity("name")
*/
class Company
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Media", mappedBy="company")
*/
private $logo;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $createdAt;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $updatedAt;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $deletedAt;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Store", mappedBy="company")
*/
private $stores;
public function __construct()
{
$this->stores = new ArrayCollection();
$this->stories = new ArrayCollection();
$this->logo = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getLogo(): ?string
{
return $this->logo[0];
}
public function setLogo(string $logo): self
{
$this->logo = $logo;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTimeInterface $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
public function getDeletedAt(): ?\DateTimeInterface
{
return $this->deletedAt;
}
public function setDeletedAt(?\DateTimeInterface $deletedAt): self
{
$this->deletedAt = $deletedAt;
return $this;
}
/**
* #return Collection|Store[]
*/
public function getStores(): Collection
{
return $this->stores;
}
public function addStore(Store $store): self
{
if (!$this->stores->contains($store)) {
$this->stores[] = $store;
$store->setCompany($this);
}
return $this;
}
public function removeStore(Store $store): self
{
if ($this->stores->contains($store)) {
$this->stores->removeElement($store);
// set the owning side to null (unless already changed)
if ($store->getCompany() === $this) {
$store->setCompany(null);
}
}
return $this;
}
public function addLogo(Media $logo): self
{
if (!$this->logo->contains($logo)) {
$this->logo[] = $logo;
$logo->setCompany($this);
}
return $this;
}
public function removeLogo(Media $logo): self
{
if ($this->logo->contains($logo)) {
$this->logo->removeElement($logo);
// set the owning side to null (unless already changed)
if ($logo->getCompany() === $this) {
$logo->setCompany(null);
}
}
return $this;
}
}
My repository :
class CompanyRepository extends AbstractRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Company::class);
}
public function findByStoreIsNational() {
return $this->createQueryBuilder('c')
->leftJoin('c.stores', 's')
->where('s.isNational = true')
->getQuery()
->getResult();
}
I thank you in advance for your help,
Regards,
Romain
As stated in the documentation for custom queries, you need to create a custom resolver, not a custom data provider.
CompaniesCollectionDataProvider should become CompanyCollectionResolver and should implement QueryCollectionResolverInterface.

Argument 1 passed to Illuminate\\Database\\Eloquent\\Model::__construct() must be of the type array, object given

I want to add product to wishlist and following repository pattern to store product id and user id of the user who wants to add product to his wishlist but when i run my code i get
Argument 1 passed to
Illuminate\Database\Eloquent\Model::__construct() must be of the
type array, object given, called in .... WishlistRepository.php
This is my Code:
WishlistController.php
namespace App\Http\Controllers\Site;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Contracts\WishlistContract;
class WishlistController extends Controller
{
protected $wishlistRepository;
public function __construct(WishlistContract $wishlistRepository)
{
$this->wishlistRepository = $wishlistRepository;
}
public function show()
{
$wishlist = $this->wishlistRepository->listWishlist();
return view('site.pages.wishlist', compact('wishlist'));
}
public function addToWishlist(Request $request)
{
$productID=$request->p_id;
$userID=auth()->user()->id;
$data=array('product_id'=>$productID,'user_id'=>$userID);
$wishlist = $this->wishlistRepository->addToWishlist($data);
}
}
WishlistContract.php
<?php
namespace App\Contracts;
/**
* Interface WishlistContract
* #package App\Contracts
*/
interface WishlistContract
{
/**
* #param string $order
* #param string $sort
* #param array $columns
* #return mixed
*/
public function listWishlist(string $order = 'id', string $sort = 'desc', array $columns = ['*']);
/**
* #param array $params
* #return mixed
*/
public function addToWishlist(array $params);
/**
* #param $id
* #return mixed
*/
//public function deleteFromWishlist($id);
/**
* #param array $params
* #return bool
*/
//public function updateWishlist(array $params);
}
?>
WishlistRepository.php
<?php
namespace App\Repositories;
use App\Models\Wishlist;
use App\Traits\UploadAble;
use Illuminate\Http\UploadedFile;
use App\Contracts\WishlistContract;
use Illuminate\Database\QueryException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Doctrine\Instantiator\Exception\InvalidArgumentException;
/**
* Class WishlistRepository
*
* #package \App\Repositories
*/
class WishlistRepository extends BaseRepository implements WishlistContract
{
use UploadAble;
public function __construct(Wishlist $model)
{
parent::__construct($model);
$this->model = $model;
}
public function listWishlist(string $order = 'id', string $sort = 'desc', array $columns = ['*'])
{
return $this->all($columns, $order, $sort);
}
public function addToWishlist(array $params)
{
try {
$collection = collect($params);
$wishlist = new Wishlist($collection);
$wishlist->save();
return $wishlist;
} catch (QueryException $exception) {
throw new InvalidArgumentException($exception->getMessage());
}
}
public function removefromWishlist($id)
{
$wishlist = $this->findAttributeById($id);
$wishlist->delete();
return $wishlist;
}
public function updateToWishlist(array $params)
{
$wishlist = $this->findWishlistById($params['id']);
$collection = collect($params)->except('_token');
$is_filterable = $collection->has('is_filterable') ? 1 : 0;
$is_required = $collection->has('is_required') ? 1 : 0;
$merge = $collection->merge(compact('is_filterable', 'is_required'));
$wishlist->update($merge->all());
return $wishlist;
}
}
?>
view (script):
$('body').on('click', '.wishlist', function(e){
e.preventDefault();
var product_id=$(this).attr('data-wishlist');
jQuery.ajax({
url: "{{ url('/add-wishlist') }}",
method: 'post',
data: {
"p_id": product_id,"_token": "{{ csrf_token() }}",
},
success: function(result){
// input.val(result.status);
}});
});
You may fix it by removing this line
$collection = collect($params);
You don't need it : because collect will convert your array to Illuminate\Support\Collection object.
Class WishlistRepository
public function addToWishlist(array $params) {
try {
$wishlist = new Wishlist($params);
// OR
// $wishlist = new Wishlist();
// $wishlist->column = $params['column'];
//...
$wishlist->save();
return $wishlist;
} catch (QueryException $exception) {
throw new InvalidArgumentException($exception->getMessage());
}
}
public function addToWishlist(array $params)
{
try {
$collection = collect($params);
$wishlist = new Wishlist($collection);
$wishlist->save();
return $wishlist;
} catch (QueryException $exception) {
throw new InvalidArgumentException($exception->getMessage());
}
}
You are passing $collection to a model whe it's expecting array with values.
When you are making the new instance of Wishlist pass in the array as it is, instead of creating and passing a collection
public function addToWishlist(array $params)
{
try {
return (new Wishlist($params))->save();
} catch (QueryException $exception) {
throw new InvalidArgumentException($exception->getMessage());
}
}

Resources