Closure Compiler no longer able to determine type - google-closure-compiler

I've been using Google's Closure Compiler for most of my projects, and a few of them in advanced mode with 100% typed.
One of my projects though no longer gets stated as 100% typed and I get warnings for things that I didn't used to get them for, and I can't seem to be able to figure out why. This is the message I get
WARNING - could not determine the type of this expression
v['children'].map(v => new _ChildLineItem(v['child'], v['option'], v['quantity'], v['price'])))))),
^
There's 42 more warnings like it, all about the same code, which I have here
/**
* #constructor
* #param {Array<!_Quote>} quotes
* #param {string} email
* #param {?string} quoteid
*/
function _GetQuotes(quotes, email, quoteid) {
this.quotes = quotes;
this.email = email;
this.quoteid = quoteid;
}
/**
* #constructor
* #param {string} quoteid
* #param {boolean} shipping
* #param {Array<!_Proof>} proofs
* #param {Array<!_LineItem>} lineitems
*/
function _Quote(quoteid, shipping, proofs, lineitems) {
this.quoteid = quoteid;
this.shipping = shipping;
this.proofs = proofs;
this.lineitems = lineitems;
}
/**
* #constructor
* #param {string} number
* #param {string} main
* #param {string} thumbnail
*/
function _Proof(number, main, thumbnail) {
this.number = number;
this.main = main;
this.thumbnail = thumbnail;
}
/**
* #constructor
* #param {string} name
* #param {number} quantity
* #param {number} price
* #param {Array<!_ChildLineItem>} children
* */
function _LineItem(name, quantity, price, children) {
this.name = name;
this.quantity = quantity;
this.price = price;
this.children = children;
}
/**
* #constructor
* #param {string} child
* #param {string} option
* #param {number} quantity
* #param {number} price
* */
function _ChildLineItem(child, option, quantity, price) {
this.child = child;
this.option = option;
this.quantity = quantity;
this.price = price;
}
Ajax({
url: '/ajax/getquotes',
data: Data,
success: function (/** !_GetQuotes */ data) {
var d = new _GetQuotes(
data['quotes'].map(v => new _Quote(v['quoteid'], v['shipping'],
v['proofs'].map(v => new _Proof(v['number'], v['main'], v['thumbnail'])),
v['lineitems'].map(v => new _LineItem(v['name'], v['quantity'], v['price'],
v['children'].map(v => new _ChildLineItem(v['child'], v['option'], v['quantity'], v['price'])))))),
data['email'], data['quoteid']);
...
I can rewrite the closures to specify the types of the objects coming through like this
v['children'].map(function( /** !_ChildLineItem */ v) { new _ChildLineItem(v['child'], v['option'], v['quantity'], v['price'])}
But shouldn't it be able to figure that out from the constructor definitions?
Actually me specifying all of them like this isn't even working
var d = new _GetQuotes(
data['quotes'].map((/** !_Quote */ v) => new _Quote(v['quoteid'], v['shipping'],
v['proofs'].map((/** !_Proof */ v) => new _Proof(v['number'], v['main'], v['thumbnail'])),
v['lineitems'].map((/** !_LineItem */ v) => new _LineItem(v['name'], v['quantity'], v['price'],
v['children'].map((/** !_ChildLineItem */ v) => new _ChildLineItem(v['child'], v['option'], v['quantity'], v['price'])))))),
data['email'], data['quoteid']);
With this warning
WARNING - could not determine the type of this expression
v['children'].map((/** !_ChildLineItem */ v) => new _ChildLineItem(v['child'], v['option'], v['quantity'], v['price'])))))),
^^^^^^^^^^^^^^^^^

The problem you are facing is that the compiler generally treats computed and dot property accesses differently. Computed property accesses are treated as the 'unknown' type unless you give a type signature the [] operator (i.e. the class implements IArrayLike as Array does or IObject[2] for map like objects).
Although, the compiler could understand that x.foo and x['foo'] are reference the same property, it does not currently do this.
[1] https://github.com/google/closure-compiler/wiki/Special-types-in-the-Closure-Type-System

Related

add button and update sales order using netsuite

I am new to netsuite,i need to update sales order by adding an item to it using a button click i had write the code for button using user event script and code for updating sales order using client script.but my client script is not working
/**
* #NApiVersion 2.x
* #NScriptType ClientScript
* #NModuleScope SameAccount
*/
define(['N/currentRecord','N/record'],
function(record) {
function buttonclick() {
try{
var CurrRecord = currentRecord.get();
var recIdSO= CurrRecord.id;
var salesOrder = record.load({
type: record.Type.SALES_ORDER,
id:recIdSO ,
isDynamic: true
});
log.debug({
title: 'recordid',
details: 'Id: ' + recIdSO
});
var line=salesOrder.selectNewLine({
sublistId: 'item'
});
salesOrder.setCurrentSublistValue({
sublistId : 'item',
fieldId : 'item',
value :510 ,
ignoreFieldChange: true
});
salesOrder.setCurrentSublistValue({
sublistId : 'item',
fieldId : 'amount',
value :100 ,
ignoreFieldChange: true
});
salesOrder.commitLine({
sublistId: 'item'
});
var recId = salesOrder.save();
log.debug({
title: 'Record updated successfully',
details: 'Id: ' + recId
});
}catch (e) {
log.error({
title: e.name,
details: e.message
});
}
}
/**
* Function to be executed after page is initialized.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.mode - The mode in which the record is being accessed (create, copy, or edit)
*
* #since 2015.2
*
function pageInit(scriptContext) {
}
/**
* Function to be executed when field is changed.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
* #param {string} scriptContext.fieldId - Field name
* #param {number} scriptContext.lineNum - Line number. Will be undefined if not a sublist or matrix field
* #param {number} scriptContext.columnNum - Line number. Will be undefined if not a matrix field
*
* #since 2015.2
*
function fieldChanged(scriptContext) {
}
/**
* Function to be executed when field is slaved.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
* #param {string} scriptContext.fieldId - Field name
*
* #since 2015.2
*
function postSourcing(scriptContext) {
}
/**
* Function to be executed after sublist is inserted, removed, or edited.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
*
* #since 2015.2
*
function sublistChanged(scriptContext) {
}
/**
* Function to be executed after line is selected.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
*
* #since 2015.2
*
function lineInit(scriptContext) {
}
/**
* Validation function to be executed when field is changed.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
* #param {string} scriptContext.fieldId - Field name
* #param {number} scriptContext.lineNum - Line number. Will be undefined if not a sublist or matrix field
* #param {number} scriptContext.columnNum - Line number. Will be undefined if not a matrix field
*
* #returns {boolean} Return true if field is valid
*
* #since 2015.2
*
function validateField(scriptContext) {
}
/**
* Validation function to be executed when sublist line is committed.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
*
* #returns {boolean} Return true if sublist line is valid
*
* #since 2015.2
*
function validateLine(scriptContext) {
}
/**
* Validation function to be executed when sublist line is inserted.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
*
* #returns {boolean} Return true if sublist line is valid
*
* #since 2015.2
*
function validateInsert(scriptContext) {
}
/**
* Validation function to be executed when record is deleted.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
*
* #returns {boolean} Return true if sublist line is valid
*
* #since 2015.2
*
function validateDelete(scriptContext) {
}
/**
* Validation function to be executed when record is saved.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #returns {boolean} Return true if record is valid
*
* #since 2015.2
*/
function saveRecord(scriptContext) {
}
return {
saveRecord: saveRecord,
buttonclick:buttonclick
};
});
Your current example is almost correct.
You need a couple of things:
first add fireSlavingSync:true as a property for all your setCurrentLineItemValue methods. Otherwise Netsuite might not have finished updating the item's field by the time you get to your commitLine call.
I'd also tend to set ignoreFieldChange:false.
you probably need to set a price level on the line item. Depending on your version of Netsuite you should be able to just set a custom price level:
salesOrder.setCurrentSublistValue({
sublistId : 'item',
fieldId : 'price',
value :-1 ,
ignoreFieldChange: false,
fireSlavingSync:true
});
AFAIK, if you add buttons from client script, they are only available in EDIT mode and you are trying to load same record and update it, this would result in record has been changed error message on UI(Also, NetSuite doesn't recommend this). The better way would be to use workflows to update records.
In workflow you could add button using the workflow itself and on button click fire your workflow action script to update line item on the Order.
Here is sample you can refer to.
/**
* #NApiVersion 2.x
* #NScriptType ClientScript
* #NModuleScope SameAccount
*/
define(['N/currentRecord', 'N/record'],
function (currentRecord, record) {
function pageInit(scriptContext) {
}
function buttonclick() {
try {
var salesOrder = currentRecord.get();
console.log(salesOrder)
var recIdSO = salesOrder.id;
/* var salesOrder = record.load({
type: record.Type.SALES_ORDER,
id:recIdSO ,
isDynamic: true
});*/
/* console.log('recordid'+ recIdSO);
*/ /*log.debug({
title: 'recordid',
details: 'Id: ' + recIdSO
}); */
var line = salesOrder.selectNewLine({
sublistId: 'item'
});
console.log('line' + line);
salesOrder.setCurrentSublistValue({
sublistId: 'item',
fieldId: 'item',
value: 510,
ignoreFieldChange: true
});
salesOrder.setCurrentSublistValue({
sublistId: 'item',
fieldId: 'amount',
value: 100,
ignoreFieldChange: true
});
salesOrder.setCurrentSublistValue({
sublistId: 'item',
fieldId: 'taxcode',
value: -160,
ignoreFieldChange: true
});
salesOrder.commitLine({
sublistId: 'item'
});
var recId = salesOrder.save();
log.debug({
title: 'Record updated successfully',
details: 'Id: ' + recId
});
} catch (e) {
console.log('recordid' + recIdSO);
/*log.error({
title: e.name,
details: e.message
});*/
}
}
return {
pageInit: pageInit,
buttonclick: buttonclick
};
});

Symfony2 data transformer string to entity (reverseTransform)

I am pretty new to Symfony and hope someone can help me. I have an entity called Material and an associated entity called MaterialKeyword, which are basically tags. I am displaying the keywords comma delimited as a string in a text field on a form. I created a data transformer to do that. Pulling the keywords from the database and displaying them is no problem, but I have a problem with the reversTransform function when I want to submit existing or new keywords to the database.
Material class (MaterialKeyword):
/**
* #Assert\Type(type="AppBundle\Entity\MaterialKeyword")
* #Assert\Valid()
* #ORM\ManyToMany(targetEntity="MaterialKeyword", inversedBy="material")
* #ORM\JoinTable(name="materials_keyword_map",
* joinColumns={#ORM\JoinColumn(name="materialID", referencedColumnName="materialID", nullable=false)},
* inverseJoinColumns={#ORM\JoinColumn(name="keywordID", referencedColumnName="id", nullable=false)})
*/
public $materialkeyword;
/**
* Constructor
*/
public function __construct()
{
$this->MaterialKeyword = new ArrayCollection();
}
/**
* Set materialkeyword
*
* #param array $materialkeyword
*
*/
public function setMaterialkeyword(MaterialKeyword $materialkeyword=null)
{
$this->materialkeyword = $materialkeyword;
}
/**
* Get materialkeyword
*
* #Assert\Type("\array")
* #return array
*/
public function getMaterialkeyword()
{
return $this->materialkeyword;
}
Here is my code from the data transformer:
This part is working:
class MaterialKeywordTransformer implements DataTransformerInterface
{
/**
* #var EntityManagerInterface
*/
private $manager;
public function __construct(ObjectManager $manager)
{
$this->manager = $manager;
}
/**
* Transforms an object (materialkeyword) to a string.
*
* #param MaterialKeyword|null $materialkeyword
* #return string
*/
public function transform($material)
{
$result = array();
if (null === $material) {
return '';
}
foreach ($material as $materialkeyword) {
$result[] = $materialkeyword->getKeyword();
}
return implode(", ", $result);
}
This part is not working:
/**
* Transforms a string (keyword) to an object (materialkeyword).
*
* #param string $materialkeyword
* #return MaterialKeyword|null
* #throws TransformationFailedException if object (materialkeyword) is not found.
*/
public function reverseTransform($keywords)
{
// no keyword? It's optional, so that's ok
if (!$keywords) {
return;
}
$repository = $this->manager
->getRepository('AppBundle:MaterialKeyword');
$keyword_array = explode(", ", $keywords);
foreach($keyword_array as $keyword){
$materialkeyword = new MaterialKeyword();
$keyword_entry = $repository->findBy(array('keyword' => $keyword));
if(array_key_exists(0, $keyword_entry)){
$keyword_entry_first = $keyword_entry[0];
}else{
$keyword_entry_first = $keyword_entry;
}
if (null === $keyword_entry_first) {
throw new TransformationFailedException(sprintf('There is no "%s" exists',
$keywords
));
}
$materialkeyword->setKeyword($keyword_entry_first);
}
return $materialkeyword;
}
There will be several keywords, so how do I store them. I tried Arrays and ArrayCollections (new ArrayCollection()) without any success.
The error that I am getting currently with the code above:
Catchable Fatal Error: Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be of the type array, object given, called in /.../vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php on line 605 and defined
TL;DR;
Your reverseTransform function should return an array containing zero or n MaterialKeyword.
It should not return a single MaterialKeyword object because the reverse transformation of MaterialKeyord[] --> string is not string --> MaterialKeyword, it is string --> MaterialKeyword[].
Thinking about this, the doctrine ArrayCollection exception you have make sense as it is trying to do new ArrayCollection(/** Single MaterialKeyword object */) instead of new ArrayCollection(/** Array of MaterialKeyword objects */).
From what you're telling I assume that Material and MaterialKeyword are connected by a ManyToMany association, in which case each Material has an array of MaterialKeyword objects associated to it.
Which means, that your Data Transformer should work with arrays as well, but you're only working with single objects.
Specifically, reverseTransform should return an array of MaterialKeyword objects, whereas you're only returning one (the last one handled in the loop.)
Another issue is that your method created new objects every time, even though $repository->findBy(...) would already return a MaterialKeyword instance. Creating a new object would cause that entry to be copied instead of simply used.
So the correct method might look like this:
public function reverseTransform($keywords)
{
// no keyword? It's optional, so that's ok
if (!$keywords) {
return array();
}
$repository = $this->manager
->getRepository('AppBundle:MaterialKeyword');
$keyword_array = explode(", ", $keywords);
$result_list = array(); // This will contain the MaterialKeyword objects
foreach($keyword_array as $keyword){
$keyword_entry = $repository->findOneBy(array('keyword' => $keyword));
if (null === $keyword_entry) {
throw new TransformationFailedException(sprintf('There is no "%s" exists',
$keyword
));
}
$result_list[] = $keyword_entry;
}
return $result_list;
}
#Hanzi put me on the correct track. It has to be an array of MaterialKeywords objects.
Here is my final working code in class MaterialKeywordTransformer:
/**
* Transforms a string (keyword) to an object (materialkeyword).
*
* #param string $materialkeyword
* #return MaterialKeyword|null
* #throws TransformationFailedException if object (materialkeyword) is not found.
*/
public function reverseTransform($keywords)
{
// keyword are optional, so that's ok
if (!$keywords) {
return;
}
$repository = $this->manager
->getRepository('AppBundle:MaterialKeyword');
$repository_m = $this->manager
->getRepository('AppBundle:Material');
$keyword_array = explode(", ", $keywords);
foreach($keyword_array as $keyword){
$materialkeyword = new MaterialKeyword();
$materialkeyword->setKeyword($keyword);
if($this->opt["data"]->getMaterialID() !== null) {
$materialkeyword->setMaterialID($this->opt["data"]->getMaterialID());
} else {
$material = $repository_m->findOne();
$materialID = $material[0]->getMaterialID();
$materialkeyword->setMaterialID($materialID);
}
$materialkeywords[] = $materialkeyword;
if (null === $keywords) {
throw new TransformationFailedException(sprintf('There is no "%s" exists',
$keywords
));
}
}
return $materialkeywords;
}

Override Method or EventListener: stop creation process and show warning just the first time in EasyAdmin?

I am using EasyAdmin in my SF 3.3 project but I need to achieve something different from how EasyAdmin has been built for. Take a look at the following picture:
As you might notice a user can be in more than one GroupingRole. Having that information the challenge is:
Check if the user has been assigned to any other GroupingRole
If the criteria meets the condition then show a warning message saying "The user A is already assigned to GroupingRole A" and prevent the record to be created. (this message could be in a popup, a javascript alert or an alert from Bootstrap - since EA already uses it)
When the admin click once again on "Save changes" the record should be created.
What I want to achieve with this approach is to alert the admin that the user is already to any other group but not stop him for create the record.
I have achieve some part of it already by override the prePersist method for just that entity (see below):
class AdminController extends BaseAdminController
{
/**
* Check if the users has been assigned to any group
*/
protected function prePersistGroupingRoleEntity($entity)
{
$usersToGroupRoleEntities = $this->em->getRepository('CommonBundle:UsersToGroupRole')->findAll();
$usersToGroupRole = [];
/** #var UsersToGroupRole $groupRole */
foreach ($usersToGroupRoleEntities as $groupRole) {
$usersToGroupRole[$groupRole->getGroupingRoleId()][] = $groupRole->getUsersId();
}
$usersInGroup = [];
/** #var Users $userEntity */
foreach ($entity->getUsersInGroup() as $userEntity) {
foreach ($usersToGroupRole as $group => $users) {
if (\in_array($userEntity->getId(), $users, true)) {
$usersInGroup[$group][] = $userEntity->getId();
}
}
}
$groupingRoleEnt = $this->em->getRepository('CommonBundle:GroupingRole');
$usersEnt = $this->em->getRepository('CommonBundle:Users');
$message = [];
foreach ($usersInGroup as $group => $user) {
foreach($user as $usr) {
$message[] = sprintf(
'The user %s already exists in %s group!',
$usersEnt->find($usr)->getEmail(),
$groupingRoleEnt->find($group)->getName()
);
}
}
}
}
What I don't know is how to stop the record to be created and instead show the warning just the first time the button is clicked because the second time and having the warning in place I should allow to create the record.
Can any give me some ideas and/or suggestions?
UPDATE: adding entities information
In addition to the code displayed above here is the entities involved in such process:
/**
* #ORM\Entity
* #ORM\Table(name="grouping_role")
*/
class GroupingRole
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer",unique=true,nullable=false)
* #ORM\GeneratedValue
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="role_name", type="string", nullable=false)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="role_description", type="string", nullable=false)
*/
private $description;
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Schneider\QuoteBundle\Entity\Distributor", inversedBy="groupingRole")
* #ORM\JoinTable(name="grouping_to_role",
* joinColumns={
* #ORM\JoinColumn(name="grouping_role_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="DistributorID", referencedColumnName="DistributorID", nullable=false)
* }
* )
*
* #Assert\Count(
* min = 1,
* minMessage = "You must select at least one Distributor"
* )
*/
private $distributorGroup;
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="CommonBundle\Entity\Users", inversedBy="usersGroup")
* #ORM\JoinTable(name="users_to_group_role",
* joinColumns={
* #ORM\JoinColumn(name="grouping_role_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="users_id", referencedColumnName="users_id", nullable=false)
* }
* )
*
* #Assert\Count(
* min = 1,
* minMessage = "You must select at least one user"
* )
*/
private $usersInGroup;
/**
* Constructor
*/
public function __construct()
{
$this->distributorGroup = new ArrayCollection();
$this->usersInGroup = new ArrayCollection();
}
}
/**
* #ORM\Entity()
* #ORM\Table(name="users_to_group_role")
*/
class UsersToGroupRole
{
/**
* #var int
*
* #ORM\Id()
* #ORM\Column(type="integer",nullable=false)
* #Assert\Type(type="integer")
* #Assert\NotNull()
*/
protected $usersId;
/**
* #var int
*
* #ORM\Id()
* #ORM\Column(type="integer", nullable=false)
* #Assert\Type(type="integer")
* #Assert\NotNull()
*/
protected $groupingRoleId;
}
A little example by using form validation approach in EasyAdminBundle:
class AdminController extends EasyAdminController
{
// ...
protected function create<EntityName>EntityFormBuilder($entity, $view)
{
$builder = parent::createEntityFormBuilder($entity, $view);
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$data = $event->getData();
$flag = false;
if (isset($data['flag'])) {
$flag = $data['flag'];
unset($data['flag']);
}
$key = md5(json_encode($data));
if ($flag !== $key) {
$event->getForm()->add('flag', HiddenType::class, ['mapped' => false]);
$data['flag'] = $key;
$event->setData($data);
}
});
return $builder;
}
protected function get<EntityName>EntityFormOptions($entity, $view)
{
$options = parent::getEntityFormOptions($entity, $view);
$options['validation_groups'] = function (FormInterface $form) {
if ($form->has('flag')) {
return ['Default', 'CheckUserGroup'];
}
return ['Default'];
};
$options['constraints'] = new Callback([
'callback' => function($entity, ExecutionContextInterface $context) {
// validate here and adds the violation if applicable.
$context->buildViolation('Warning!')
->atPath('<field>')
->addViolation();
},
'groups' => 'CheckUserGroup',
]);
return $options;
}
}
Note that PRE_SUBMIT event is triggered before the validation process happen.
The flag field is added (dynamically) the first time upon submitted the form, so the validation group CheckUserGroup is added and the callback constraint do its job. Later, the second time the submitted data contains the flag hash (if the data does not changes) the flag field is not added, so the validation group is not added either and the entity is saved (same if the callback constraint does not add the violation the first time).
Also (if you prefer) you can do all this inside a custom form type for the target entity.

Symfony 2 - Entity is not updated

I am working with form aimed at uploading the file and updating the database in Symfony2. I want to manually set value of book_id field and not to allow user to change it in the form. Thus in my controller before using doctrine to persist document I am calling:
$documents->setBookId('1');
Unluckilly I get error which indicates that the doctrine does not recognise the above hard coded value input.
An exception occurred while executing 'INSERT INTO Documents (book_id, marker, document_date, link, notes) VALUES (?, ?, ?, ?, ?)' with params [null, "fdd", "2015-04-04", null, "test"]:
To my mind this may be connected with the fact that book_id field is related to Books. Therefore probably I should use setBook function instead. Could you please advice how to do this properly?
My controler file looks like this:
/**
* This code is aimed at checking if the book is chosen and therefore whether any further works may be carried out
*/
$session = new Session();
if(!$session->get("App_Books_Chosen_Lp")) return new RedirectResponse($this->generateUrl('app_listbooks'));
// Authorization goes here
$documents = new Documents();
$form = $this->createForm(new DocumentsType(), $documents);
$form->add('save', 'submit', array('label' => 'Dodaj dokument'));
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$documents->upload();
$documents->setBookId('1');
$em->persist($documents);
$em->flush();
}
return $this->render('AppBundle:Documents:adddocuments.html.twig', array('form' => $form->createView()));
Document class:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* #ORM\Entity
* #ORM\Table(name="Documents")
* #ORM\HasLifecycleCallbacks
*/
class Documents
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Books", inversedBy="documents")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
protected $book;
/**
* #ORM\Column(type="integer")
*/
protected $book_id;
/**
* #ORM\Column(type="string", length=220)
*/
protected $marker;
/**
* #ORM\Column(type="date", length=220)
*/
protected $document_date;
/**
* #ORM\Column(type="string", length=220)
* #Assert\File(maxSize="6000000")
*/
protected $link;
/**
* #ORM\Column(type="text")
*/
protected $notes;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set book_id
*
* #param integer $bookId
* #return Documents
*/
public function setBookId($bookId)
{
$this->book_id = $bookId;
return $this;
}
/**
* Get book_id
*
* #return integer
*/
public function getBookId()
{
return $this->book_id;
}
/**
* Set marker
*
* #param string $marker
* #return Documents
*/
public function setMarker($marker)
{
$this->marker = $marker;
return $this;
}
/**
* Get marker
*
* #return string
*/
public function getMarker()
{
return $this->marker;
}
/**
* Set document_date
*
* #param \DateTime $documentDate
* #return Documents
*/
public function setDocumentDate($documentDate)
{
$this->document_date = $documentDate;
return $this;
}
/**
* Get document_date
*
* #return \DateTime
*/
public function getDocumentDate()
{
return $this->document_date;
}
/**
* Set link
*
* #param string $link
* #return Documents
*/
public function setLink($link)
{
$this->link = $link;
return $this;
}
/**
* Get link
*
* #return string
*/
public function getLink()
{
return $this->link;
}
/**
* Set notes
*
* #param string $notes
* #return Documents
*/
public function setNotes($notes)
{
$this->notes = $notes;
return $this;
}
/**
* Get notes
*
* #return string
*/
public function getNotes()
{
return $this->notes;
}
/**
* Set book
*
* #param \AppBundle\Entity\Books $book
* #return Documents
*/
public function setBook(\AppBundle\Entity\Books $book = null)
{
$this->book = $book;
return $this;
}
/**
* Get book
*
* #return \AppBundle\Entity\Books
*/
public function getBook()
{
return $this->book;
}
/*
* ### FILE UPLOAD PROCESS ###
*/
/**
* #Assert\File(maxSize="6000000")
*/
private $file;
/**
* Sets file.
*
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file = null)
{
$this->file = $file;
}
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile()
{
return $this->file;
}
public function getAbsolutePath()
{
return null === $this->path
? null
: $this->getUploadRootDir().'/'.$this->path;
}
public function getWebPath()
{
return null === $this->path
? null
: $this->getUploadDir().'/'.$this->path;
}
protected function getUploadRootDir()
{
// the absolute directory path where uploaded
// documents should be saved
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
protected function getUploadDir()
{
// get rid of the __DIR__ so it doesn't screw up
// when displaying uploaded doc/image in the view.
return 'uploads/documents';
}
public function upload()
{
// the file property can be empty if the field is not required
if (null === $this->getFile()) {
return;
}
// use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and then the
// target filename to move to
$this->getFile()->move(
$this->getUploadRootDir(),
$this->getFile()->getClientOriginalName()
);
// set the path property to the filename where you've saved the file
$this->path = $this->getFile()->getClientOriginalName();
// clean up the file property as you won't need it anymore
$this->file = null;
}
}
Okay, first since you're using ManyToOne relation, you don't actually need another property refering to the book - book_id. You can remove that and leave book only.
Then in your controller you have to query the database for that Book and set the that object your Document.
You can do it like this:
$bookId = 1; // Following your example, let's say tou already know the book ID.
$book = $em->getReference('AppBundle:Books', $bookId);
// Check if we actually found a record and then set it to Documents
// Looking at your entity mapping, your reference to Book can not be null,
// but doing an extra check never hurts, since this is just an example.
if( $book ) {
$documents->setBook($book);
}
-Update-
If you want to directly insert the bookID, then what is the purpose of having ManyToOne reference in your entity? Eventually you're going to have to start using doctrine's relations and objects properly. Also, the cool thing about getReference method is that you are getting a reference to an entity, without having to load the entity from the database - you get the so called Proxy objects.
The method EntityManager#getReference($entityName, $identifier) lets you obtain a reference to an entity for which the identifier is known, without loading that entity from the database. This is useful, for example, as a performance enhancement, when you want to establish an association to an entity for which you have the identifier
You can read further about this here.

fosRestBundle : Send Array into Http Request Header

Aim : Send an Http request as an array (i'm looking to the uri syntax if it's possible to send an array by the header of the Http)
Server Side
ProfilHandler.php My Profil Handler
/**
* Get a list of News.
*
* #param int $limit the limit of the result
* #param int $offset starting from the offset
* #param array $sector get profils by sectors
*
* #return array
*/
public function all($limit = 5, $offset = 0,$sector)
{
return $this->repository->findBy(array(), null, $limit, $offset, $sector);
}
ProfilController.php My Profil Controller
/**
* List all profiles.
*
* #ApiDoc(
* resource = true,
* statusCodes = {
* 200 = "Returned when successful"
* }
* )
*
* #Annotations\QueryParam(name="offset", requirements="\d+", nullable=true, description="Offset from which to start listing profiles.")
* #Annotations\QueryParam(name="limit", requirements="\d+", default="10", description="How many pages to return.")
* #Annotations\QueryParam(name="sector", requirements="\d+", default="IT", description="How many pages to return.")
*
* #Annotations\View(
* templateVar="profiles"
* )
*
* #param Request $request the request object
* #param ParamFetcherInterface $paramFetcher param fetcher service
*
* #return array
*/
// ....
public function getProfilsAction(Request $request, ParamFetcherInterface $paramFetcher)
{
$offset = $paramFetcher->get('offset');
$offset = null == $offset ? 0 : $offset;
$limit = $paramFetcher->get('limit');
$secteur = $paramFetcher->get('sector');
return $this->container->get('genius_profile.profil.handler')->all($limit, $offset,$sector);
}
Yes, it is possible. For example:
Construct url like this:
url -> http://your-app.dev/app_dev.php/some/route/?options[city]=1231&options[county]=3432
then in controller for some/route action you can get
$arr = $request->get('options', array());
And then $arr should conatin:
Array
(
[city] => 1231
[county] => 3432
)
[Edit]:
Maybe try this:
Remove requirements for sector (or set it to match comma separeted list requirements="[\w,]+":
* #Annotations\QueryParam(name="sector", default="IT", description="How many pages to return.")
Then construct url with sector as an comma separated list, for example
?offset=5&limit=10&sector=IT,HEALTH
And inside controller retrive it like this:
$secteur = explode(',', $paramFetcher->get('sector'));
[Edit2]:
public function all($limit = 5, $offset = 0,$secteur) {
if (sizeof($secteur)>=1){
return $this->repository->findBy(array('secteur' => $secteur), array('secteur' => 'ASC'));
}
return $this->repository->findBy(array(), null, $limit);
}

Resources