I'm trying to get the value from Index controller.
Controller\Adminhtml\Orders\Index.php :
public function execute()
{
$storeid = $this->getRequest()->getParam('storeid');
$this->settings->setStoreid($storeid);
}
Model\Settings.php
public function setStoreid($storeid)
{
$this->storeid = $storeid;
}
public function getStoreid()
{
return $this->storeid;
}
Model\ResourceModel\Order\Grid\Collection.php :
public function _renderFiltersBefore{
$storeid = $this->getStoreId();
// var_dump($storeid)
// $storeid = 5
$this->getSelect()->where('store_id = ?', $storeid);
}
I want to pass the storeid to the query Collection. I can able to set the storeid from Index class and can able to get the storeid in Collection class. But it doesnt passing to the collection.
If I set the value directly to storeid, ie. $storeid = 5, then collection works fine. Please advice.
Maybe try to use registry
/**
* #var \Magento\Framework\Registry
*/
protected $_registry;
/**
* ...
* ...
* #param \Magento\Framework\Registry $registry,
*/
public function __construct(
...,
...,
\Magento\Framework\Registry $registry,
...
) {
$this->_registry = $registry;
...
...
}
/**
* Setting custom variable in registry to be used
*
*/
public function setCustomVariable()
{
$this->registry->register('custom_var', 'Added Value');
}
/**
* Retrieving custom variable from registry
* #return string
*/
public function getCustomVariable()
{
return $this->registry->registry('custom_var');
}
Related
I have two entities, MediaObject and Book. MediaObject is a generic entity for managing files and includes fields like size, mimeType, and filePath. Book has fields like title, author, and also includes a link to an associated MediaObject for its cover image file.
How can I POST a Book entity with its associated MediaObject cover image with API-Platform? I'd like to do this as one atomic operation. I don't want books saved without a cover image and I don't want orphan cover images. So I don't want to POST a MediaObject cover image and then use the ID I get back when POSTing a new Book. (or vice-versa)
https://api-platform.com/docs/core/file-upload/
class MediaObject
{
...
public $filePath;
...
}
class Book
{
...
public $coverImage; // i.e. mediaObjectId; associated MediaObject to an image file
...
}
The documentation has the option "deserialize"= false. This means that deserialization will not occur for this operation. Therefore, you must write the entire deserialization process yourself to the handler controller. You must also write fields for the swagger documentation.
For example:
<?php
declare(strict_types=1);
namespace App\Entity;
// more use...
/**
* #ApiResource(
* iri="http://schema.org/MediaObject",
* normalizationContext={
* "groups" = {"media:read"}
* },
* collectionOperations={
* "post" = {
* "controller" = MediaHandler::class,
* "deserialize" = false,
* "access_control" = "is_granted('ROLE_USER')",
* "validation_groups" = {"Default", "media:collection:post"},
* "openapi_context" = {
* "requestBody" = {
* "content" = {
* "multipart/form-data" = {
* "schema" = {
* "type" = "object",
* "properties" = {
* "file" = {
* "type" = "string",
* "format" = "binary"
* },
* "name" = {
* "type" = "string"
* }
* }
* }
* }
* }
* }
* }
* },
* "get"
* },
* itemOperations={
* "get"
* }
* )
* #Vich\Uploadable
* #ORM\Entity(repositoryClass="App\Repository\MediaRepository")
*/
class Media
{
/**
* #ApiProperty(iri="http://schema.org/contentUrl")
* #Groups({"media:read"})
*/
public $contentUrl;
/**
* #Assert\NotNull(groups={"media:collection:post"})
* #Vich\UploadableField(mapping="media", fileNameProperty="filePath")
* #Assert\File(
* maxSize="2M",
* mimeTypes={
* "application/pdf",
* "application/x-pdf",
* "image/jpeg",
* "image/jpg",
* "image/png"
* },
* groups={"media:collection:post"}
* )
*/
public $file;
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=512)
*/
private $filePath;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
//...
}
Controller handler example:
<?php
declare(strict_types=1);
namespace App\Controller\Api;
// use ...
class MediaHandler extends AbstractController
{
/**
* #var EntityManagerInterface
*/
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function __invoke(Request $request): Media
{
$uploadedFile = $request->files->get('file');
if (!$uploadedFile) {
throw new BadRequestHttpException('"file" is required');
}
$mediaObject = new Media();
$mediaObject->file = $uploadedFile;
$mediaObject->setName($request->request->get('name'));
return $mediaObject;
}
}
If the "Book" exists. And you want to add Book toMediaObject, you can set the iri string and parse it in the controller-handler:
//...
public function __construct(EntityManagerInterface $entityManager, IriConverterInterface $iriConverter)
{
$this->entityManager = $entityManager;
$this->iriConverter = $iriConverter;
}
public function __invoke(Request $request): Media
{
$uploadedFile = $request->files->get('file');
if (!$uploadedFile) {
throw new BadRequestHttpException('"file" is required');
}
$iriBook = $request->request->get('book');
$book = null;
if ($iriBook) {
/**
* #var Book $book
*/
$book = $this->iriConverter->getItemFromIri($iriBook);
}
$mediaObject = new Media();
$mediaObject->file = $uploadedFile;
$mediaObject->setBook($book);
return $mediaObject;
}
//..
If this is your case, then no further action (DataPersist) is required.
Next your need to go https://api-platform.com/docs/core/data-persisters/ and make DataPesist handler
Example:
<?php
declare(strict_types=1);
namespace App\DataPersister;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use App\Entity\Media;
use App\ExtendTrait\ContextAwareDataTrait;
use Doctrine\ORM\EntityManagerInterface;
class MediaObjectDataPersister implements ContextAwareDataPersisterInterface
{
use ContextAwareDataTrait;
/**
* #var EntityManagerInterface
*/
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* {#inheritdoc}
*/
public function supports($data, array $context = []): bool
{
return $this->isCollection('post', $context) && $data instanceof Media;
}
/**
* {#inheritdoc}
*
* #param $data Media
*
* #throws \Exception
*/
public function persist($data, array $context = []): void
{
$book = new Book();
$book->setName($data->getName());
// begin transaction and persist and flush $book and $data
}
/**
* {#inheritdoc}
*/
public function remove($data, array $context = []): void
{
// todo remove book
}
}
P.S. I don't test this code. I writing idea ;)
P.S.S. $this->isCollection() it function from my trait, may be need you it:
<?php
declare(strict_types=1);
namespace App\ExtendTrait;
/**
* Trait ContextAwareDataTrait.
*
* Helps confirm the operation name
*/
trait ContextAwareDataTrait
{
public function isItem(string $operationName, array $context, string $resourceClass = null): bool
{
if ($resourceClass && ($context['resource_class'] ?? false) !== $resourceClass) {
return false;
}
return ($context['item_operation_name'] ?? null) === $operationName;
}
public function isCollection(string $operationName, array $context, string $resourceClass = null): bool
{
if ($resourceClass && ($context['resource_class'] ?? false) !== $resourceClass) {
return false;
}
return ($context['collection_operation_name'] ?? null) === $operationName;
}
}
I need to hyrdate multiple objests in one form. Here is what I use:
Product Form - I have a form where I call three fieldsets
Product Fieldset
Promotion Fieldset
Category Fieldset
I have Models for all the necessary tables, here is an example for the product model:
class Product implements ProductInterface
{
/**
* #var int
*/
protected $Id;
/**
* #var string
*/
protected $Title;
/**
* #var float
*/
protected $Price;
/**
* #var string
*/
protected $Description;
/**
* #var string
*/
protected $Url;
/**
* #var \DateTime
*/
protected $DateAdded;
/**
* #var string
*/
protected $Image;
/**
* #var int
*/
protected $Status;
/**
* #return int
*/
public function getId()
{
return $this->Id;
}
/**
* #param int $Id
*/
public function setId($Id)
{
$this->Id = $Id;
}
/**
* #return string
*/
public function getTitle()
{
return $this->Title;
}
/**
* #param string $Title
*/
public function setTitle($Title)
{
$this->Title = $Title;
}
/**
* #return float
*/
public function getPrice()
{
return $this->Price;
}
/**
* #param float $Price
*/
public function setPrice($Price)
{
$this->Price = $Price;
}
/**
* #return string
*/
public function getDescription()
{
return $this->Description;
}
/**
* #param string $Description
*/
public function setDescription($Description)
{
$this->Description = $Description;
}
/**
* #return string
*/
public function getUrl()
{
return $this->Url;
}
/**
* #param string $Url
*/
public function setUrl($Url)
{
$this->Url = $Url;
}
/**
* #return \DateTime
*/
public function getDateAdded()
{
return $this->DateAdded;
}
/**
* #param \DateTime $DateAdded
*/
public function setDateAdded($DateAdded)
{
$this->DateAdded = $DateAdded;
}
/**
* #return string
*/
public function getImage()
{
return $this->Image;
}
/**
* #param string $Image
*/
public function setImage($Image)
{
$this->Image = $Image;
}
/**
* #return int
*/
public function getStatus()
{
return $this->Status;
}
/**
* #param int $Status
*/
public function setStatus($Status)
{
$this->Status = $Status;
}
In my controllers I want to bind the data to my view so I can edit them.
try {
$aProduct = $this->productService->findProduct($iId);
} catch (\Exception $ex) {
// ...
}
$form = new ProductForm();
$form->bind($aProduct);
In the first place I need to select all the necessary information from the DB. I join three tables product, promotion and category tables. I must return the data to my controller as objects and bind them in my form to be able to edit on the view page.
Please give me some ideas how to accomplish this so I can continue with my development. I am stuck.
I will appreciate all the links which can help me or give me any ideas/examples from the real life.
public function findProduct($Id)
{
$iId = (int) $Id;
$sql = new Sql($this->dbAdapter);
$select = $sql->select('product');
$select->join('promotion', 'promotion.ProductId = product.Id', array('Discount', 'StartDate', 'EndDate', 'PromotionDescription' => 'Description', 'PromotionStatus', 'Type'), 'left');
$select->join('producttocategory', 'producttocategory.ProductId = product.Id', array('CategoryId'), 'left');
$select->join('category', 'category.Id = producttocategory.CategoryId', array('ParentId', 'Title', 'Description', 'Url', 'DateAdded', 'Image', 'Status'), 'left');
$where = new Where();
$where->equalTo('product.Id', $iId);
$select->where($where);
$stmt = $sql->prepareStatementForSqlObject($select);
$result = $stmt->execute();
if ($result instanceof ResultInterface && $result->isQueryResult()) {
$resultSet = new HydratingResultSet($this->hydrator, $this->productPrototype);
return $resultSet->initialize($result);
}
throw new \Exception("Could not find row $Id");
}
I need to hydrate the result and return an object which I will use in the controller to bind the form.
You can to fill entities from a database manually.
If you want to fill automatically need to create a map between a database and entities. I made a library for making a map between DB and entities use annotations in entities https://github.com/newage/annotations.
Next step.
When you get different data from tables. Example:
SELECT
table1.id AS table1.id,
table1.title AS table1.title,
table2.id AS table2.id,
table2.alias AS table2.alias
FROM table1
JOIN table2 ON table1.id = table2.id
Need do foreach by rows and set data to entities comparing row with table name and Entity from a generated map.
Auto generating tree of entities from DB is my next project.
But it's do not finished. https://github.com/newage/zf2-simple-orm.
I have 3 tables:
Category ( id, name)
Category_Tournament (category_id, tournament_id) --> pivot table
Category_Tournament_User (category_id, tournament_id, user_id, confirmed)
Category is the list of available categories
Category_Tournament is the list of categories the admin configured
Category_tournament_User is the categories the user has registred
To get all categories in the tournament, I can do it easily with:
tournament->categories
defining a belongsToMany relationship in tournament model
What I don't know how to define relationship with the last table.
What I need is the user click on several categories, and I can run something like:
tournament->user_categories->sync($userCategories)
where I should sync table Category_Tournament_User ( with category_id, tournament_id, user_id)
What is the best way to achieve it???
EDIT:
Model Tournament:
class Tournament extends Model
{
protected $table = 'tournament';
public $timestamps = true;
protected $fillable = [
'name',
'date',
'type',
];
/**
* A tournament is owned by a user
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function owner()
{
return $this->belongsTo('App\User', 'user_id','id');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function categories()
{
return $this->belongsToMany('App\Category')
->withTimestamps();
}
}
Model Category
class Category extends Model
{
protected $table = 'category';
public $timestamps = true;
protected $fillable = [
'id',
'name',
];
public function tournaments()
{
return $this->belongsToMany('App\Tournament');
}
}
Model User:
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword, HasRole;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['name','firstname','lastname','email', 'password','avatar',country_id','role_id',,'provider','provider_id','verified'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = ['password', 'remember_token'];
/**
* Boot the model.
*
* #return void
*/
public static function boot()
{
parent::boot();
static::creating(function ($user) {
$user->token = str_random(30);
});
}
public function role()
{
return $this->belongsTo('App\Role');
}
public function settings()
{
return $this->hasOne('App\Settings');
}
public function invites()
{
return $this->hasMany('App\Invite', 'email','email');
}
public function country()
{
return $this->belongsTo('Webpatser\Countries\Countries');
}
/**
* A user can have many tournaments
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function tournaments()
{
return $this->hasMany('App\Tournament');
}
}
You have many to many relationship here between User and Category_Tournament and you should take a look in documentation exactly at Many To Many.
I think you don't need to to have Category_Tournament_User table. and you can't make a Model for it in Laravel. you only need to a table user_tournament. and you should define relation(foreign key) on migration, like this:
Schema::create('user_tournament', function(Blueprint $table){
$table->engine = 'InnoDB';
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('tournament_id')->unsigned();
$table->unique(['tournament_id', 'user_id']);//You can omit this
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
$table->foreign('tournament_id')->references('id')->on('tournaments')->onDelete('cascade')->onUpdate('cascade');
$table->nullableTimestamps();
});
then you can use this code:
user->tournaments->sync($userCategories);
I'm trying to play with MongoDB. But I've got some problems.
# src/Controller/DefaultController.php
public function editCategoryAction(Category $category) {
echo $category->getName();
die();
}
With "entity" this is ok but not with MongoDB.
It displays this error message :
Controller "Site\StoreBundle\Controller\DefaultController::editCategoryAction()" requires that you provide a value for the "$category" argument (because there is no default value or because there is a non optional argument after this one).
My Category Document :
namespace Site\StoreBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class Category
{
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\String
*/
protected $name;
/**
* #MongoDB\ReferenceMany(targetDocument="Product")
*/
private $products = array();
public function __contructor() {
$this->products = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return id $id
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return self
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string $name
*/
public function getName()
{
return $this->name;
}
public function getProducts() {
return $this->products;
}
public function __construct()
{
$this->products = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add product
*
* #param Site\StoreBundle\Document\Product $product
*/
public function addProduct(\Site\StoreBundle\Document\Product $product)
{
$this->products[] = $product;
}
/**
* Remove product
*
* #param Site\StoreBundle\Document\Product $product
*/
public function removeProduct(\Site\StoreBundle\Document\Product $product)
{
$this->products->removeElement($product);
}
}
My Route
editCategory:
path: /editCategory/{id}
defaults: { _controller: SiteStoreBundle:Default:editCategory }
How can I solve this ?
Edit:
I found this article : http://php-and-symfony.matthiasnoback.nl/2012/10/symfony2-mongodb-odm-adding-the-missing-paramconverter/
I changed my src/Site/StoreBundle/Resources/config/services.yml to this :
parameters:
# site_store.example.class: Site\StoreBundle\Example
services:
doctrine_mongo_db_param_converter:
class: Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DoctrineParamConverter
arguments: ['#doctrine_mongodb']
But same error :/
I think you missed to tag your service:
services:
doctrine_mongo_db_param_converter:
class: Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DoctrineParamConverter
arguments: ['#doctrine_mongodb']
tags:
- { name: request.param_converter, priority: -2, converter: doctrine_mongo_db_param_converter}
I'm trying to create symfony2 application which will access mongoDB collection with the following structure:
{
"_id": ObjectId("5239c1c0359bf908058a5071"),
"param2": "test",
"param3": {
"subparam31": 0,
"subparam32": 0,
"subparam33": 0
},
"param4": 1
}
In symfony 2 I create a .yml, and php class. I correctly map only "_id", "param2" and "param4", but not "subparam31", "subparam32" and "subparam33" of "param3".
I use next file structure for mapping:
Params.mongodb.yml:
Acme\StoreBundle\Document\Params:
db: test
type: document
fields:
id:
id: true
param2:
type: string
param3:
type: mixed
subparam31:
type: float
subparam32:
type: float
subparam33:
type: float
param4:
type: float
Params.php
<?php
namespace Acme\StoreBundle\Document;
class Params
{
protected $param2;
protected $param4;
protected $param3;
protected $subparam31;
protected $subparam32;
protected $subparam33;
}
?>
Where I'm wrong? How to get and set values of subparams?
For accessing param2, param4 and id I use following code in controller which works:
$parameter = $this->get('doctrine_mongodb')
->getRepository('AcmeStoreBundle:Params')
->find($id);
$parameter2 = $parameter->getParam2();
$parameter4 = $parameter->getParam4();
if (!$format) {
throw $this->createNotFoundException('Not found parameter with id -> '.$id);
}
return array(
"parameter2" => $parameter2,
"parameter4" => $parameter4
);
I hope I was clear enough.
Thanks in advance.
I found the solution! In addition to mapping in yml, appropriate annotation in php classes also should be defined.
Here is the content the necessary files:
Params.mongodb.yml
Acme\StoreBundle\Document\Params:
db: test
type: document
embedOne:
param3:
targetDocument: Param3
fields:
id:
id: true
param2:
type: string
param4:
type: float
Param3.mongodb.yml
Acme\StoreBundle\Document\Param3:
db: test
type: embeddedDocument
fields:
subparam31:
type: float
subparam32:
type: float
subparam33:
type: float
Params.php
<?php
namespace Acme\StoreBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations;
/**
* #Doctrine\ODM\MongoDB\Mapping\Annotations\Document
*/
class Params
{
/**
* #Doctrine\ODM\MongoDB\Mapping\Annotations\Id
*/
protected $id;
/**
* #Doctrine\ODM\MongoDB\Mapping\Annotations\String
*/
protected $param2;
/**
* #Doctrine\ODM\MongoDB\Mapping\Annotations\Float
*/
protected $param4;
/**
* #Doctrine\ODM\MongoDB\Mapping\Annotations\EmbedOne(targetDocument="Param3")
*/
protected $param3;
public function __construct($subparam31 = NULL, $subparam32 = NULL, $subparam33 = NULL)
{
$param3 = new Param3($subparam31, $subparam32, $subparam33);
$this->setParam3($param3);
}
/**
* Get id
*
* #return id $id
*/
public function getId()
{
return $this->id;
}
/**
* Set param2
*
* #param string $param2
* #return self
*/
public function setParam2($param2)
{
$this->param2 = $param2;
return $this;
}
/**
* Get param2
*
* #return string $param2
*/
public function getParam2()
{
return $this->param2;
}
/**
* Set param4
*
* #param float $param4
* #return self
*/
public function setParam4($param4)
{
$this->param4 = $param4;
return $this;
}
/**
* Get param4
*
* #return float $param4
*/
public function getParam4()
{
return $this->param4;
}
/**
* Set param3
*
* #param Acme\StoreBundle\Document\Param3 $param3
* #return self
*/
public function setParam3(\Acme\StoreBundle\Document\Param3 $param3)
{
$this->param3 = $param3;
return $this;
}
/**
* Get param3
*
* #return Acme\StoreBundle\Document\Param3 $param3
*/
public function getParam3($toArray = false)
{
if ($toArray) {
if ($this->param3) {
return $this->param3->toArray();
}
}
return $this->param3;
}
public function toArray()
{
return array(
'param3' => $this->getParam3(true)
);
}
}
Param3.php
<?php
namespace Acme\StoreBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations;
/**
* #Doctrine\ODM\MongoDB\Mapping\Annotations\EmbeddedDocument
*/
class Param3
{
/**
* #Doctrine\ODM\MongoDB\Mapping\Annotations\Float
*/
protected $subparam31;
/**
* #Doctrine\ODM\MongoDB\Mapping\Annotations\Float
*/
protected $subparam32;
/**
* #Doctrine\ODM\MongoDB\Mapping\Annotations\Float
*/
protected $subparam33;
public function __construct($subparam31 = NULL, $subparam32 = NULL, $subparam33 = NULL)
{
$this->subparam31 = $subparam31;
$this->subparam32 = $subparam32;
$this->subparam33 = $subparam33;
}
/**
* Set subparam31
*
* #param float $subparam31
* #return self
*/
public function setSubparam31($subparam31)
{
$this->subparam31 = $subparam31;
return $this;
}
/**
* Get subparam31
*
* #return float $subparam31
*/
public function getSubparam31()
{
return $this->subparam31;
}
/**
* Set subparam32
*
* #param float $subparam32
* #return self
*/
public function setSubparam32($subparam32)
{
$this->subparam32 = $subparam32;
return $subparam32;
}
/**
* Get subparam32
*
* #return float $subparam32
*/
public function getSubparam32()
{
return $this->subparam32;
}
/**
* Set subparam33
*
* #param float $subparam33
* #return self
*/
public function setSubparam33($subparam33)
{
$this->subparam33 = $subparam33;
return $this;
}
/**
* Get subparam33
*
* #return float $subparam33
*/
public function getSubparam33()
{
return $this->subparam33;
}
public function toArray()
{
return array(
'subparam31' => $this->getSubparam3(),
'subparam32' => $this->getSubparam32(),
'subparam33' => $this->getSubparam33()
);
}
}
This question helped me.
I think what you're looking for is an EmbeddedDocument.
Define a separate document for param3 (that includes subparam31, subparam32, and subparam33) set it as the targetDocument in params. So Params.mongodb.yml would look something like:
db: test
type: document
embedOne:
params3:
targetDocument: params3Class
fields:
id:
id: true
param2:
type: string
param4:
type: float