Imagine I have a "Product" Class that has 3 properties (creationTimestamp, name and price).
I do not want the user to change the name once the object has been created. No problem for that: I added an Event Subscriber to the FormType.
In addition, I do never show the creationTimestamp in my create and edit forms. I want this field to be populated "in the background" only when I create the object. It should not change when I edit my object. Question: should I put the logic in the Controller or somewhere else (ie. a Lifecycle Callback). What is the best practice ? A syntax example would be really appreciated. Thank you in advance
You could do it in the constructor.
class Product
{
public function __construct()
{
$nowTimestamp = 123456789; //Here your timestamp value
$this->setCreationTimestamp($nowTimestamp); //Assuming that you have defined setter function
}
}
Related
I want to initialize a value of an edit method inside the init method of form, i wrote this:
[Form]
public class foo extends FormRun
{
str paymTermId;
public void init()
{
CustTable custTable = CustTable::find("DE-001");
paymTermId = custTable.paymTermId;
super();
}
edit str edtpaymTermId(boolean set, str _paymTermId)
{
if (set)
{
paymTermId= _paymTermId;
}
return paymTermId ;
}
}
But when i open the form the control remains empty.
any suggestions?
I tried to reproduce the issue, but was not successful. For me, when opening the form, the control shows a value.
A possible reason why it is not working for you could be that you open the form in the wrong company. In your code, you retrieve the value to display in the control from the payment term of customer DE-001. This customer exists in company USMF in the Contoso demo data and has payment term Net10. If the form is opened in this company, the value is shown in the control. If you are in another company (e.g. DAT), no value is shown.
I see 2 things that are wrong:
You are setting the value BEFORE super(). It should be after.
You SHOULDN'T initialize the value via field, you should do it calling edit method. Edit methods have a boolean SET parameter which can simulate a call for setting a value.
Context
I have a custom Event Entity which has several child Entities: Problem and Maintenance (and few others but those two should be enough to describe the problem) entity classes inherit from Event entity class.
The addAction(), seeAction() and modifyAction() of ProblemController and MaintenanceController are (obviously) very similar but with some differences.
I want to have a button to display the see view of an Event, no matter if it is a Problem or a Maintenance. Same for modify.
For the add action it is a bit different: the user has to say (by clicking on child-specific button) what kind of child he want to add.
How I handle this so far
In my seeAction() and modifyAction(), I just forward the "call" depending on the type of the child:
public function seeAction(Event $event)
{
if($event instanceof \Acme\EventBundle\Entity\Problem){
return $this->forward('AcmeEventBundle:Problem:see', array('event_id' => $event->getId()));
}
elseif($event instanceof \Acme\EventBundle\Entity\Maintenance){
return $this->forward('AcmeEventBundle:Maintenance:see', array('maintenance_id' => $event->getId()));
}
}
I have no Event::addAction() but I have a Event::addCommon() which gathers the common parts of the addAction of Problem and Maintenance. Then I call this Event::addCommon() with Controller inheritance.
class ProblemController extends EventController
{
public function addAction(MeasurementSite $measurementSite)
{
$problem = new Problem();
$problem->setMeasurementSite($measurementSite);
$form = $this->createForm(new ProblemType($measurementSite), $problem);
$response = parent::addCommon($problem, $form);
return $response;
}
Problem
All this looks pretty ugly to me. If I want to share common things between Problem::seeAction() and Maintenance::seeAction(), I will have to call an Event function, but Event already forwarded something!! Information jumps from Parent to Child and vice versa...
I would like to know what is the proper way to manage this problem?
I looked a bit at setting Controller as a service, using PHP Traits, Routing inheritance but I couldn't extract anything clear and clean from this research...
I can see how you might end up chasing your tail on this sort of problem.
Instead of multiple controllers, consider have one EventController for all the routes along with individual ProblemHelper and MaintainenceHelper objects. The helper objects would have your add/see/modify methods and could extend a CommonHelper class.
Your controller would check the entity type, instantiate the helper and pass control over to it.
I've got an issue with my fieldset in Zend Framework 2.
The user is able to save his personal data over a form. If he already save this, they should be prefilled with the data from database. This worked fine as it was only a form, but I need the address data in a fieldset, so that I can use it at other parts of my program. Now the input fields stays empty.
At the beginning, I fill the personal data in a session. My data looks like this:
object(Application\Model\Product\PersonalData)#247 (3) {
["tel":protected]=> string(0) ""
["birthday":protected]=> string(10) "2013-01-01"
["address":protected]=> object(Application\Model\Account\Addresses)#248 (15) {
["firstname":protected]=> string(5) "Ernie"
["surname":protected]=> string(6) "Muppet"
...
}
}
As you can see, the data is already bind to the given objects, PersonalData as main, and Addresses for the fieldset. This seems to work then.
Then I put it in my form:
$oForm->setData($oForm->getHydrator()->extract($_SESSION->getPersonalData()));
return new ViewModel(array('form'=>$oForm));
The addressFieldset has a hydrator and a binding, which does work, because all objects are perfectly filled. The only problem is, that when I open the page, the input-fields are empty, only birthday and telephone are filled, which are directly on the form
My form implements the address-fieldset like this:
$addressFieldset = new AddressFieldset($lang);
$addressFieldset->setUseAsBaseFieldset(true);
$addressFieldset->setName('address');
$this->add($addressFieldset);
I think that it might be just a problem with the correct addressing of my fieldset, but I can't explain why it would be filled correctly after posting the data then. All I want is that he fill the setData in my Fieldset.
I hope you understand my question and could help me.
Thanks a lot,
Svenja
EDIT:
I analysed it a bit more now, it's very strange and I don't know what went wrong.
When I debug I can see that $fieldset->getValue() returns all data I need. So I thought that the binding might be wrong and I did this to debug it step by step:
$values = $this->form->get('address')->getValue();
$addressFieldset = $this->form->get('address');
$aValues = $addressFieldset->getHydrator()->extract($values);
$addressFieldset->bindValues($aValues);
I went to the Fieldset.php and bindValues does perfectly what it should (it is only a recapitulation anyway(?)), call the hydrator and fill my object. But if I show in the elements, all values are NULL.
I already checked my elements twice. The only different between the model and the elements is a different order of declaration. I call the method setUseAsBaseField(true) in the fieldset and the form, too. I can't understand why the data is in values but not in elements.
It's very strange, because I have something and it good work. Are you confident that the expression $_SESSION->getPersonalData() to return the desired result? You are using a very strange session.
http://framework.zend.com/manual/2.1/en/modules/zend.session.container.html
To EDIT (I'm sorry my english):
You can use different types of hydrator, for example
ArraySerializable (by default, your entity must have getArrayCopy()
and exchangeArray() methods, in your case) ArraySerializable is the hydrator by default.
First you have to bind a form with entity $form->bind(new Entity()); The entity will bind to the base fieldset. If the base fieldset not specified, the entity will bind with the form, because the form inherits fieldset. If the entity is an aggregate, ie, its properties contain another objects, for each of these objects should be your fildset.
In constructor this children fieldsets you should use
$this->setObject(new MyChildrenEntity());
As a result, the entity properties will be extracted to the form
elements.
After that, you should only work with the form, but not with its
elements or fieldsets.
You can pass any data in the form, so form elements will get this
values. $form->setData($this->getRequest()->getPost()); This method use internal populateValues() method. If the form has elements with an appropriate name, they will be assigned to these values. If the form has fieldsets, they will also be transferred to these values.
As long as the form fails to validate, the entity of these values
will not be assigned. These values are assigned to entities only
in case of successful verification. IsValid () method uses the
internal method bindValues () if the validation was successful.
After successful validation, you can get the entity using getData()
method $entity = $form->getData();
P.S.: If you are doing a complex "haсk", do not be offended by this simple explanation.
I finally found the solution!
At first, this link helped me:
Populating fieldsets
I didn't integrate the fieldset Input-Filter in the Form Input Filter.
But that wasn't all I have to do. My Form-Datamodel looks like this:
interface IPersonalData
{
public function getTel();
public function getBirthday();
public function getAddress();
public function setTel($tel);
public function setAddress(IAddresses $address);
public function setBirthday($birthday);
}
This is only the interface, but Address is an object. And that is the problem. When the form tries to fill the fieldset, he only accept arrays. So I have to extract my object in the getter-method to an array.
I don't think that this is very useful, because I normaly want to get my object with this method. So I write a method "getAddressAsArray()" which looks like this:
public function getAddressAsArray()
{
$oAddressHydrator = new AddressHydrator();
if(isset($this->address))
{
return $oAddressHydrator->extract($this->address);
}
return array();
}
The extract-method of my hydrator changed like this:
public function extract($object)
{
if(!$object instanceof IPersonalData)
{
throw new \InvalidArgumentException('$object must be an instance of Application\Model\Product\IPersonalData');
}
return array(
'telephone' => $object->getTel(),
'address' => $object->getAddressAsArray(),
'birthday' => $object->getBirthday(),
);
}
I need to add an additional field to InventJournalTrans, that after posting will show up in the InventTrans table. The field is a reference column to a record in another table. What method(s) do I need to modify to make this behavior happen?
Currently, I have already added the fields to both tables and modified the Form to allow the user to enter and save the new field. I just can't seem to find the bottom of the rabbit hole on where the actual posting to InventTrans is occurring.
Ideally, it should just be a:
inventTrans.ReasonRefRecId = inventJournalTrans.ReasonRefRecId;
assignment statement before the
inventTrans.insert();
call. Anybody have a clue on where this is at?
The link above does contain the solution -- I have included the code from that page in case that page disappears or no longer becomes available. Thanks to gl00mie for answering on that site and providing this answer.
You should create a new InventMovement method like this:
public MyNewFieldType myNewField()
{
return MyNewFieldType::DefaultValue; // suppose your new field is an enum
}
Then modify \Classes\InventMovement\initInventTransFromBuffer
void initInventTransFromBuffer(InventTrans _inventTrans, InventMovement _movement_orig)
{
// ... append this line to the end of whatever else is already in this method
_inventTrans.MyNewField = this.myNewField();
}
And finally overload the new method in the InventMov_Journal class:
public MyNewFieldType myNewField()
{
return inventJournalTrans.MyNewField;
}
I have a question regarding a data binding(of multiple properties) for custom DataGridViewColumn.
Here is a schema of what controls that I have, and I need to make it bindable with DataGridView datasource. Any ideas or a link to an article discussing the matter?
Controls
Graph Control(custom): Displayed in
the custrom DataGridView column. Has
properties like "Start Date",
"EndDate", Windows Chart control,
which is itself, bindable, etc.
Custom cell(DataGridViewCustomCell inherits
from DataGridViewCell) that holds
the Graph control and processes some
events(OnEnter event, for example,
passes the focus to the custom Graph
column for drag-n-drop type of
events, etc.)
Custom column(DataGridViewCustomColumn
inherits from DataGridViewColumn)
that defined the cell template type:
CellTemplate = new
DataGridViewCustomCell(); and also a
primary choice for data binding
Data Structure:
Main table to be displayed in other DataGridView Columns
Graph table - related to the Main table via parent-child relationship. Holds graph data
Chart table related to the graph table via parent-child relationship. Holds data for the win-form chart, which is a part of my Graph control.
So far I cannot even bind data from the Graph table to by Graph control or Graph-holding Column/Cell.
Thank you for your answer. My data sources is not a SQL data source, and as a matter of fact I was talking about datagridview for win-forms(I'm not sure that was clear).
As I did not get the reply on any of the forums I was asking the question, I figured, I would outline a solution I came up with, for those who may have a similar problem and for possible critique. :-)
(steps 1-2 are also explained in the famous MS example)
1. Create your own classes that inherit from DataGridViewColumn and DataGridViewCell, setup the column template;
2. Create your "CustomEdit" control
In the data item, whatever that is, a DataRow, or a List item, add a read-only property, that return the object itself. This property is bound to the custom column.
Custom Cell:
public partial class MyCell : DataGridViewCell
{
protected override void Paint(...)
{...} // draws control
// receives data item as a value
// in my case I have to custom-draw entire control in this fnc.
public override void InitializeEditingControl(...)
{...} // initialize control editing
// override some other properties
public override Type EditType {
get{
return typeof(MyEditControl);
}
}
public override Type ValueType{
get{
return typeof(MyItem);
}
}
}
Custom Column:
public partial class MyColumn : DataGridViewColumn
{
public MyColumn(){ ...
CellTemplate = new MyCell();
}
}
Edit Control:
public partial class MyEditControl : UserControl, IDataGridViewEditingControl
{... // implements IDataGridViewEditingControl
// value is our data item
}
Data Item, the data sources becomes List<MyItem>
public class MyItem:Object{
...
[XmlIgnore] // I need it because I do serialization
public MyItem Self {
get {
return this;
}
}
}
See my question Here
It's easy to do, you just don't use the IDE to do it, you do it all in code. It's a lot of work, but it's not that difficult if you know what your doing. I went from knowing nothing to being able to do it in less than a day so I'm sure you'll be able to do it.
Edit: you can also use a Join in the sql that populates the datagridview