I'm struggling with flushing my zend_form to 2 entities. In total I've 3 entities. User, Address and Country. Country is not interesting because it has standard values where only the ID come into Address. My add-action works good (only when I keep my address/country empty, it puts a new record in the database so I've to fix this). But my edit only flushes my latest bind. My edit-action:
/**
* Edit action for single user
*
* #return route zfcadmin/user/add
* #return route zfcadmin/user
* #return array(id, form, flashMessages)
*/
public function editAction()
{
$id = (int)$this->getEvent()->getRouteMatch()->getParam('id');
if (!$id) {
return $this->redirect()->toRoute('zfcadmin/user/', array('action' => 'add'));
}
//Get user with the id (given in URL), and pick his address(es)
$user = $this->getEntityManager()->find('User\Entity\User', $id);
$addresses = $user->addresses;
$form = new UserForm();
//Set country values out of the entity
$form = $this->fillCountrySelectbox($form);
$form->bind($user);
//Get addresses from the user, and bind it to the $form
foreach($addresses as $address) {
//If user has address, bind the addresses
if(isset($address)) {
$form->bind($address);
}
}
$form->get('save_goback')->setAttribute('value', 'Save');
$form->get('save')->setAttribute('value', 'Save & Stay');
$request = $this->getRequest();
if ($request->isPost()) {
//Set the inputfilter on the input values
$inputFilter = new InputFilter();
//Set filters from different entities
$inputFilter = $user->setFilters($inputFilter);
$inputFilter = $address->setFilters($inputFilter);
//Set the inputFilter on the form
$form->setInputFilter($inputFilter);
$form->setData($request->getPost());
if ($form->isValid()) {
$form->bindValues();
//set complete country object in address entity.
$country = $this->getEntityManager()->find('User\Entity\Country', $form->get('country')->getValue());
$address->__set('country', $country);
//Set country Null when no country was selected, otherwise it conflict with country entity (because no '' is set)
if($address->country == '') {
$address->country = NULL;
}
//Set modifier (current user)
$address->__set('last_modifier_id', $this->zfcUserAuthentication()->getIdentity()->getId());
$this->flashMessenger()->addMessage('User saved');
$this->getEntityManager()->flush();
if ($this->getRequest()->getPost('save_goback')) {
return $this->redirect()->toRoute('zfcadmin/user');
}
}
}
return array(
'id' => $id,
'form' => $form,
'flashMessages' => $this->flashMessenger()->getMessages()
);
}
When I not bind the address, it will flush to the user-entity, and when I bind the address, it flushes to address only, so my edits on the user won't work. How can I edit my form, with saving the user AND address?
Related
How exactly ID build? Data stored in store by ID, but how the ID is generated?
//Code just to pass requirements
normalize(model, hash, prop) {
hash.id = 'someId';
return this._super(...arguments);
}
If you ask about new records ( which creates on client side ) :
https://github.com/emberjs/data/blob/v2.5.3/addon/-private/system/store.js#L329
/**
If possible, this method asks the adapter to generate an ID for
a newly created record.
#method _generateId
#private
#param {String} modelName
#param {Object} properties from the new record
#return {String} if the adapter can generate one, an ID
*/
_generateId(modelName, properties) {
var adapter = this.adapterFor(modelName);
if (adapter && adapter.generateIdForRecord) {
return adapter.generateIdForRecord(this, modelName, properties);
}
return null;
},
Also check documentation for generateIdForRecord http://emberjs.com/api/data/classes/DS.Adapter.html#method_generateIdForRecord
I have an entity, call it Stones and Stones has a ManyToMany relationship with Attributes.
So I query the entity to get the Stones and then I hydrate this to convert it into an array.
$result = $this->stoneRepository->find($stone_id);
if ( ! $result )
{
return false;
}
$resultArray = $this->doctrineHydrator->extract($result);
This works fine for the Stone entity however I noticed that the join (Attributes) remain as objects.
array (size=12)
'id' => int 1
'name' => string 'Agate' (length=5)
'title' => string 'Title' (length=5)
'attribute' =>
array (size=5)
0 =>
object(Stone\Entity\StAttribute)[1935]
private 'id' => int 2
private 'name' => string 'Hay fevor' (length=9)
private 'state' => boolean true
private 'created' => null
private 'modified' => null
1 =>
object(Stone\Entity\StAttribute)[1936]
private 'id' => int 15
private 'name' => string 'Libra' (length=5)
private 'state' => boolean true
private 'created' => null
private 'modified' => null
2 =>
etc.
What is the process to hydrate the Attribute objects?
Hydration is populating an object (entity) using an array which is opposite of the extraction.
Since you want the resultset in array format, you should prevent unnecessary hydration and extraction process which already occurs in the ORM level under the hood.
Try to use Query Builder Api instead of built-in find() method of the entity repository. This is not a single-line but really straightforward and faster solution, it should work:
$qb = $this->stoneRepository->createQueryBuilder('S');
$query = $qb->addSelect('A')
->leftJoin('S.attribute', 'A')
->where('S.id = :sid')
->setParameter('sid', (int) $stone_id)
->getQuery();
$resultArray = $query->getOneOrNullResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
This way, you will also prevent running additional SQL queries against database to fetch associated entities. (StAttribute in your case)
I thought I would follow up on this to show how this can be resolved using a CustomStrategy.
By far the easiest and fastest method was suggested by foozy. What I like about the solution is that when I use hydration in ApiGility for instance I can build custom queries which will produce the desired result in a very few lines of code.
The other solution I was working on was to add a custom strategy:
<?php
namespace Api\V1\Rest\Stone;
use DoctrineModule\Stdlib\Hydrator\Strategy\AbstractCollectionStrategy;
use Zend\Stdlib\Hydrator\Strategy\StrategyInterface;
class CustomStrategy extends AbstractCollectionStrategy
{
public function __construct($hydrator)
{
$this->hydrator = $hydrator;
}
/**
* #param mixed $values
* #return array|mixed
*/
public function extract($values)
{
$returnArray = [];
foreach ($values AS $value)
{
$returnArray[] = $this->hydrator->extract($value);
}
return $returnArray;
}
/**
* #param mixed $values
* #return mixed
*/
public function hydrate($values)
{
$returnArray = [];
foreach ($values AS $value )
{
$returnArray[] = $this->hydrator->hydrate($value);
}
return $returnArray;
}
}
Then from the service side I add various strategies to the hydrator like so:
$result = $this->stoneRepository->find($stone_id);
$this->doctrineHydrator->addStrategy("product", new CustomStrategy( $this->doctrineHydrator ) );
$this->doctrineHydrator->addStrategy("attribute", new CustomStrategy( $this->doctrineHydrator ) );
$this->doctrineHydrator->addStrategy("image", new CustomStrategy( $this->doctrineHydrator ) );
$this->doctrineHydrator->addStrategy("related", new CustomStrategy( $this->doctrineHydrator ) );
$resultArray = $this->doctrineHydrator->extract($result);
After which I created a custom entity:
<?php
namespace Api\V1\Rest\Stone;
class StoneEntity
{
public $id;
public $name;
public $description;
public $code;
public $attribute;
public $product;
public $image;
public function getArrayCopy()
{
return array(
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'code' => $this->code,
'attribute' => $this->attribute,
'product' => $this->product,
'image' => $this->image
);
}
public function exchangeArray(array $array)
{
$this->id = $array['id'];
$this->name = $array['name'];
$this->description = $array['description'];
$this->code = $array['code'];
$this->attribute = $array['attribute'];
$this->product = $array['product'];
$this->image = $array['image'];
}
}
And the final part is to exchange the returned data with the custom entity:
$entity = new StoneEntity();
$entity->exchangeArray($resultArray);
And finally to return the result:
return $entity;
To be honest, the above is just too long winded and my final solution as per the suggestion by foozy was this:
public function fetchOne($stone_id)
{
$qb = $this->stoneRepository->createQueryBuilder('S');
$query = $qb->addSelect('A','P','I','C')
->leftJoin('S.attribute', 'A')
->innerJoin('A.category', 'C')
->innerJoin('S.product' , 'P')
->innerJoin('S.image' , 'I')
->where('S.id = :sid')
->setParameter('sid', (int) $stone_id)
->getQuery();
$resultArray = $query->getOneOrNullResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
if ( ! $resultArray )
{
return false;
}
return $resultArray;
}
I have a User entity and in the edit Action I present a form to edit the user. I would like to have a password field available. If the password is blank, then I would like to update all field except the password, and if something is entered then all fields would be updated.
Here is my controller action.
public function editUserAction() {
$id = (int) $this->params()->fromRoute('id', 0);
if (!id) return $this->redirect()->toRoute('index', array('action' => 'users));
$objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
$form = new UserForm($objectManager);
$user = $objectManager->find('Application\Entity\User', $id);
$form->bind($user);
if ($this->request->isPost()) {
$form->setData($this->request->getPost());
if ($form->isValid()) {
$data = $this->request->getPost();
if ($data->user['password'] == '') {
// how to save all but one field
}
else {
$objectManager->persist($user);
$objectManager->flush();
}
}
}
}
use the remove method:
vendor/zendframework/zendframework/library/Zend/Form/Fieldset.php
remove Remove a named element or fieldset
Parameters:
string
$elementOrFieldset
PHPDoc not found
Returns:
Type:
FieldsetInterface
if ($data->user['password'] == '')
{
$form->remove('password');
$objectManager->persist($user);
$objectManager->flush();
}
i am writing webservice in symfony2 but i facing some problem regarding the output ,as it is giving blank output.
class DefaultController extends Controller {
/**
*
* #Route("/webservices/activity/{id}", name="user_json_activity")
* #Method("get")
*/
public function activityAction($id) {
$em = $this->getDoctrine()->getEntityManager();
$list = $em->getRepository('FitugowebserviceBundle:activity')->findOneById($id);
$r_array = $this->routes2Array($list);
$r = array('activity' => $r_array);
return new Response(json_encode($r));
}
private function routes2Array($routes) {
$points_array = array();
foreach ($routes as $route) {
$r_array = array('activity' => $route->getActivity(),
'icon' => $route->getIcon());
$points_array[] = $r_array;
}
return $points_array;
}
}
When i try to fetch data for id=1 http://domain.org/fitugo/web/app_dev.php/webservices/activity/1 it is giving output as follows
{"activity":[]}
It look very strange that you want get array with findOneById method. The first thing I suggest to add a check that the entity founded by id exist. Then look that findOneById returns and check your controller logic.
For example, my username in the sitecore admin is "Borj", whenever I create an article I want "Borj" to be automatically fill the author field of any article I would create.
Yes, this is possible, but will require some customization.
By default you only have the following tokens:
$name: Is replaced with the name of the created item
$parentname: Is replaced with the name of the parent to the created item
$date: Is replaced with the current date
$time: Is replaced with the current time
$now: Is replaced with current date and time
$id: Is replaced with the id of the created item
$parentid: Is replaced with the id of the parent to the created item.
This post by John West shows you exactly how to fill a field with the name of the user that's creating the item.
This is the code he uses:
public class MasterVariablesReplacer : SC.Data.MasterVariablesReplacer
{
public override string Replace(string text, SC.Data.Items.Item targetItem)
{
SC.Diagnostics.Assert.ArgumentNotNull(text, "text");
SC.Diagnostics.Assert.ArgumentNotNull(targetItem, "targetItem");
string result = this.ReplaceValues(
text,
() => targetItem.Name,
() => targetItem.ID.ToString(),
() => SC.Data.Items.ItemUtil.GetParentName(targetItem),
() => targetItem.ParentID.ToString());
return result;
}
private string ReplaceValues(
string text,
Func<string> defaultName,
Func<string> defaultId,
Func<string> defaultParentName,
Func<string> defaultParentId)
{
if ((text.Length != 0) && (text.IndexOf('$') >= 0))
{
SC.Text.ReplacerContext context = this.GetContext();
if (context != null)
{
foreach (KeyValuePair<string, string> pair in context.Values)
{
text = text.Replace(pair.Key, pair.Value);
}
}
text = this.ReplaceWithDefault(text, "$name", defaultName, context);
text = this.ReplaceWithDefault(text, "$id", defaultId, context);
text = this.ReplaceWithDefault(text, "$parentid", defaultParentId, context);
text = this.ReplaceWithDefault(text, "$parentname", defaultParentName, context);
text = this.ReplaceWithDefault(text, "$date", () => SC.DateUtil.IsoNowDate, context);
text = this.ReplaceWithDefault(text, "$time", () => SC.DateUtil.IsoNowTime, context);
text = this.ReplaceWithDefault(text, "$now", () => SC.DateUtil.IsoNow, context);
text = this.ReplaceWithDefault(text, "$user", () => SC.Context.User.LocalName, context);
}
return text;
}
private string ReplaceWithDefault(
string text,
string variable,
Func<string> defaultValue,
SC.Text.ReplacerContext context)
{
if ((context != null) && context.Values.ContainsKey(variable))
{
return text;
}
if (text.IndexOf(variable, StringComparison.InvariantCulture) < 0)
{
return text;
}
return text.Replace(variable, defaultValue());
}
}
If you then change the setting MasterVariablesReplacer to your own assembly and class, it'll also pick up on $user
In this post Alistair Deneys shows a different way of doing it as well.
[edit]
Please do note that the (untested) code provided above will not work with branches - just with the 'usual' way of creating items.
Sitecore already stores the user who created an item in the __created field if you just want to display the user name on the front end web site you could just use that field and strip off the domain i.e. sitecore\