How to annotate sub-documents in Doctrine MongoDB? - mongodb

I want to implement php classes that should model the following :
(Symfony,DoctrineMongoDBBundle)
Notification Collection { from { id , fname } , to , sentDate }
Member Collection { id , fname , lname , email , phone , regDate , ... }
And i want tell to ODM : "from field is an object that holds only 2 values ,id of the sender and his fname"
What annotation i must use? Should i define another class like from.php ? Or i can create two classes as the following:
/*
*#Document
*/
class Notification {
/*
*#Id
*/
protected $id;
/*
*#EmbedOne(targetDocument="Member")
*/
protected $from;
/*
*#ReferenceOne(targetDocument="Member")
*/
protected $to;
/*
*#Date
*/
protected $sentDate;
}
/*
*#Document
*/
class Member {
/*
*#Id
*/
protected $id;
/*
*#String
*/
protected $fname;
/*
*#String
*/
protected $lname;
/*
*#String
*/
protected $email;
.
.
.
}
If it's correct,in my controller how can i control "from" field to hold only id and fname from a Member object?
suppose this:
$senderUser;
$newNotification = new Notification();
$newNotification->setFrom($senderUser);
Then $newNotification->from set to a Member object that hold all info about a Member.But i want only id and fname of the sender to persist! (because needs of my project)
Excuse my English grammatical errors.
Thanks for any help...

If you insist on nesting the notification's originating user ID and first name in a from field, then you will need an Embed One relationship to a From document, which in turn has the following fields:
id: this should be a Reference One relationship to a Member document. I would suggest using the simple option for the reference, so that you only store the Member's ID value, rather than a DBRef object.
fname: this should be a string field. ODM has no facility for keeping it up-to-date with the referenced Member document, so you will need to ensure that on your own.
Personally, the Notification document seems small enough that I would simply create fromMember and fromMemberFirstName fields directly on Notification. Then, you could add some logic in the setFromMember() method that also sets the fromMemberFirstName field from the passed Member argument.
If you want to take things a step further, you could explore using events to monitor Member objects for changed fname fields and issue queries to update Notification documents, but that's a separate conversation.

Related

I have made a category and want to make sub category, how can I add dynamically?

I made a form in which I have titlte, image, pdf, content and category and all are working fine, but I want to add submenu in my form and also want to make this dynamic so can show all sub-menu on my website.
You must have to intruduce a key parent_id with categories table.
And in category model do create the relation like this.
class Category extends Model
{
public $fillable = ['title','parent_id'];
/**
* Get the index name for the model.
*
* #return string
*/
public function childs() {
return $this->hasMany('App\Category','parent_id','id') ;
}
}
Here you can find the complete example..https://itsolutionstuff.com/post/laravel-5-category-treeview-hierarchical-structure-example-with-demoexample.html
Hopefully this could help you.

Magento 2: Get Product Stock Quantity and Other Stock Information

Magento 2: Get Product Stock Quantity and Other Stock Information
How to get the product stock quantity and information in magento 2
If we look at the StockItemRepository class the get method wants parameter $stockItemId, not $productId. Reference:
https://github.com/magento/magento2/blob/develop/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php#L202
I've seen many sites where stock item id IS NOT the same as product id and we should not assume it's the same ID.
To get this working you could use \Magento\CatalogInventory\Model\Stock\Item class instead and load the model by product_id field instead. I am also aware of the website_id and stock_id fields, but as far as I know it's not used (yet) and also existed in M1.
It should look something like this (code not tested):
<?php
namespace Vendor\Module\Model;
use \Magento\CatalogInventory\Model\Stock\Item;
class Mymodel
{
/**
* #var Item
*/
protected $stockItem;
/**
* Mymodel constructor.
*
* #param Item $stockItem
*/
public function __construct(Item $stockItem)
{
$this->stockItem = $stockItem;
}
/**
* Description
*
* #param $productModel
*/
public function getStockQtyByProductId($productModel)
{
try {
$stockItem = $this->stockItem->load($productModel->getId(), 'product_id');
return $stockItem->getQty();
} catch (\Exception $e) {
echo 'Something went wrong and was not handled: ' . $e->getMessage();
exit;
}
}
}
if you have product object then just use following:
echo $_product->getExtensionAttributes()->getStockItem()->getQty();
conplete object can be find as follow:
var_dump($_product->getExtensionAttributes()->getStockItem()->getData());
Actually this operation should be performed using \Magento\CatalogInventory\Api\StockRegistryInterface and here we can obtain \Magento\CatalogInventory\Api\Data\StockItemInterface, by product id or sku and we can use bunch of usefull methods to get stock information - linked product. For general stock information I recommend explore other service contracts declared in Magento\CatalogInventory\Api
Example of usage:
<?php
namespace Test\Test\Model;
class Test
{
protected $_stockRegistry;
public function __construct(\Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry)
{
$this->_stockRegistry = $stockRegistry;
}
public function getStockItem($productId)
{
return $this->_stockRegistry->getStockItem($productId);
}
}
this code help you to get product quantity
<?php
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$StockState = $objectManager->get('\Magento\CatalogInventory\Api\StockStateInterface');
echo $StockState->getStockQty($productId);
?>
If you have the product object and do not want to use the other classes, You can try in the following way.
// For phtml file
$prodObj = $_product->load($_product->getId()); // $_product object in list.phtml
$stockItem = $prodObj->getExtensionAttributes()->getStockItem();
$stockQty = $stockItem->getQty(); // $stockItemData = $stockItem->getData();
// For php class file
$stockItem = $prodObj->getExtensionAttributes()->getStockItem();
$stockQty = $stockItem->getQty(); // $stockItemData = $stockItem->getData();
Credits:
https://github.com/magento/magento2/issues/7057#issuecomment-256052729
Actually \Magento\CatalogInventory\Api\Data\StockStatusInterface
should answer to all your questions.
Long story short:
Magento has StockItem entity which represents amount (Qty) of specific product (productId) on a concrete stock (stockId).
StockItemInterface should be used when you would like to "write" data into the data storage (like update amount of products to sync up Magento with your ERP system or to make deduction of stock during the checkout process).
StockStatusInterface is opposite to it. It should be used to "read" data for representation (on front-end). Consider StockStatus as an index which contains aggregated stock data for each specific product.
So, if you would like to get product stock status (in stock, out of stock) by product_id.
You need using StockStatusRepositoryInterface::getList(StockStatusCriteriaInterface $searchCriteria);
get StockStatus entity for specified product
/** #var \Magento\CatalogInventory\Api\StockStatusCriteriaInterfaceFactory $stockStatusCriteriaFactory **/
$criteria = $stockStatusCriteriaFactory->create();
$criteria->setProductsFilter($productId);
/** #var \Magento\CatalogInventory\Api\Data\StockStatusRepositoryInterface $stockStatusRepository **/
$result = $stockStatusRepository->getList($criteria);
$stockStatus = current($result->getItems());
$stockStatus->getProductId(); // product id
$stockStatus->getQty(); // quantity of specified product
$stockStatus->getStockStatus(); // Could be
// Magento\CatalogInventory\Model\Stock\Status::STATUS_OUT_OF_STOCK = 0;
// or
// Magento\CatalogInventory\Model\Stock\Status::STATUS_IN_STOCK = 1;

ManyToOne with FOSUSerBundle ignoring exclusion policy

Building a JSON response for an API type thing, to retrieve a specific set of data that includes a ManyToOne relationship in the entity for my entity that extends FOSUSerBundle's User entity (called Account in my case).
The problem is, the Account entity thats included as a field in the response, is wanted, but I dont want to include all of the password and role type stuff.
I've been browing the internet for a couple hours now, and I've followed many guides on this, and I've cleared my cache every single time, and to no avail; So here's where I ended up:
// app/config/config.yml
jms_serializer:
metadata:
auto_detection: true
directories:
FOSUserBundle:
namespace_prefix: "FOS\\UserBundle"
path: "%kernel.root_dir%/Resources/serializer/FOS"
I've for below I've tried User.Model.yml and Model.User.yml and User.Entity.yml as well in a vain thought that the file name actually matters
// app/Resources/serializer/FOS/Entity.User.yml
FOS\UserBundle\Model\User:
exclusion_policy: ALL
properties:
id:
expose: true
and what I get still looks like this:
{
"status":"ok",
"api_version":"1.0",
"code":200,
"data":{
"video":{
"id":1,
"published":true,
"visibility":true,
"title":"Megaman 2",
"slug":"megaman-2",
"summary":"A rap song about Megaman",
"description":"A rap song\r\nAbout megaman",
"youtube_id":"R6L9bUouDr8",
"date_published":"2014-07-02T14:09:26-0700",
"date_created":"2014-07-02T14:09:26-0700",
"date_updated":"2014-07-02T14:09:26-0700",
"author_id":3,
"author":{
"id":3,
"username":"kharrison",
"username_canonical":"kharrison",
"email":"(sorry private)",
"email_canonical":"(sorry, private)",
"enabled":true,
"salt":"(sorry, private)",
"password":"(sorry, private)",
"last_login":"2014-07-04T15:17:34-0700",
"locked":false,
"expired":false,
"roles":[
"ROLE_SUPER_ADMIN"
],
"credentials_expired":false,
"display_name":"Kyle Harrison",
"slug":"kyle-harrison",
"bio":"Test"
}
}
}
}
The "author" field, is my Account entity thats being run through the JMSSerializer
I want to exclude ALL of that, except the user ID, Display name, and slug.
And finally this is how the API works:
// My/Bundle/Controller/BaseAPIController.php
//......... other code
/**
* #param string $status
* #param integer $code
* #return Response
*/
public function render_api($status, $code)
{
$this->apiResponse->setStatus($status);
$this->apiResponse->setCode($code);
return new Response($this->apiResponse->serialize($this->get('jms_serializer')), $this->apiResponse->getCode(), ["Content-type"=>"application/json"]);
}
//............. other code
and finally, that calls this:
// My/Bundle/Models
class APIResponse {
protected $status;
protected $apiVersion;
protected $code;
protected $data;
public function __construct($apiVersion, $status = "OK", $code = 500)
{
$this->status = $status;
$this->code = $code;
$this->apiVersion = $apiVersion;
$this->data = [];
}
// ... getters and setters
/**
* #return mixed
*/
public function serialize($serializer) {
return $serializer->serialize($this, "json");
}
}
I've for below I've tried User.Model.yml and Model.User.yml and
User.Entity.yml as well in a vain thought that the file name actually
matters.
It does matter, actually. It's a concatenation of the namespace and class name. In this case, you're trying to configure the FOS\UserBundle\Model\User class, so the file name should be Model.User.yml. (FOS\UserBundle\ should be excluded from the file name, since you configured it as namespace_prefix in your config.yml)
Also make sure that your Account class doesn't re-declare (overwrite) the properties, as the serializer config only works if you configure it for the class that actually declares the properties.
Ok So, the actual answer, couldn't have been arrived to via the information I provided. But Nic's Answer did lead me towards the solution. The description of how the the serializer looks at and deciphers the config file lead me to the real problem at hand.
This is what I failed to show:
<?php
namespace [PRIVATE]\[PRIVATE]Bundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
use JMS\Serializer\Annotation\Groups;
use JMS\Serializer\Annotation\VirtualProperty;
/**
* Account
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="[PRIVATE]\[PRIVATE]Bundle\Entity\AccountRepository")
*/
class Account extends BaseUser
{
The problem lays with the Alias I provided the FOS\UserBundle\Model\User namespace. I no longer remember why I wrote that that way. However, the moment I remove the Alias and rewrote the extends to resemble this instead:
<?php
namespace [PRIVATE]\[PRIVATE]Bundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
use JMS\Serializer\Annotation\Groups;
use JMS\Serializer\Annotation\VirtualProperty;
/**
* Account
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="[PRIVATE]\[PRIVATE]Bundle\Entity\AccountRepository")
*/
class Account extends User
{
combined with the new correct filename from Nic's answer, the config based Exclusion policy for JMSSerializerBundle totally kicks in, and every instance of FOSUserBundle's items are now completely hidden, except for the fields I've now explicitly told it to expose.
This is exactly what I wanted :)
Thanks everyone for your help! Cheers
~k
I'm not sure it's the exact way you want it, more a way around:
way around 1: Select only the properties you want (via the entity manager) and then serialize the array obtained.
It's what I do with what I call my API (which is not a class as you but controllers)

Cloning embedded document only works if value is changed

I have a Mongo database to which I am connected through the following document classes in Symfony, using Doctrine ODM (I am only showing the relevant properties).
/**
* #MongoDB\Document
* #MongoDB\InheritanceType("COLLECTION_PER_CLASS")
*/
class Feature {
/** #MongoDB\EmbedOne(targetDocument="FeatureProperties") */
protected $properties;
}
/** #MongoDB\Document */
class City extends Feature {}
/** #MongoDB\EmbeddedDocument */
class FeatureProperties {
/** #MongoDB\String */
protected $name;
}
I am trying to clone a City in my controller, including its properties, essentially its properties.name.
To do this, I get the city using QueryBuilder and call it $city_origin.
Then, here's how I clone it:
$new_city = clone $city_origin;
$city_name = $city_origin->getProperties()->getName();
// $city_name .= ' ';
$documentManager->detach($city_origin);
$new_city->setId('_' . $city_origin->getId());
$new_city->getProperties()->setName($city_name);
$documentManager->persist($new_city);
$documentManager->flush();
The new document is created, but properties and properties.name do not exist.
Now the strange thing is, if I uncomment the line where I modify the $city_name, then the properties.name is created with the modified value.
If instead of fetching the $city_name from $city_origin I input its value manually (as a string), the attribute is not created either. If I give the name of another City in my collection, there is no issue.
Ideally, I would like to clone the entire document with its embedded documents: how can I do that?
When you clone a object, you get a copy of this object whit the same reference of the embedded object.
When you do :
$documentManager->detach($city_origin);
Doctrine will detach all the object related to $city_origin because of your entity definition :
cascade=DETACH or cascade=ALL
http://docs.doctrine-project.org/en/2.0.x/reference/working-with-objects.html#detaching-entities
You need to define the clone function in the city class and clone all the object you need.
http://www.php.net/manual/en/language.oop5.cloning.php

DoctrineMongoDB : How to set annotation in a document to store a specific structure?

I am implementing a php class that must model this :
/** #Document */
classMember {
/** #String */
protected $fname;
/** #String */
protected $lname;
/** #String */
protected $email;
/** #Int */
protected $cell;
/** #String */
protected $password;
/** #Int */
protected $gender;
/** #Int */
protected $loc = array();
/** ????? */
protected $info;
}
I want info field to hold this structure in itself :
info Object
[
contact ["phoneNumber1" , "phoneNumber2"] ,
address ["USA, NY 8791 John St."] ,
email ["my#domain.com", "me#site.info"]
]
Should i implement another info.php class?If not,how can i implement this?
Thanks for any help...
$info may use an Embed One mapping. The class you embed would then have mappings for the contact, address, and email fields.
If contact and email are really intended to hold multiple phone numbers and email addresses, respectively, then I would suggest using the Collection mapping, which is an arbitrary array of unmapped values. Alternatively, these could be Embed Many relationships to PhoneNumber and Email objects, but that may be unnecessary overhead since it would entail storing an array of single-field (I assume) objects, instead of an array of strings.
I'm not sure why you represented the address field as an array of strings, but that would benefit from being an Embed Many relationship of Address objects. Address would then have specific fields (e.g. country, province/state). Allowing the user to enter their address as a single string may be convenient, but it makes data management and validation a nightmare.