ZF2 one to many relationship using Database, TableGateways and Form ElementCollection - zend-framework2

i'm searching for a strategy to create a one to many raltionship in ZF2, between two tables using TableGateways. Table one is called "workers" and one worker has many "departments".
Now i want to have a form for the workers which inlcudes a special DepartmentFieldset (Textfield to enter an ID).
I was thinking about this way:
The worker Model has one more type array for the departmentIDs, but there is no field in the table for the departmentIDs. Also there is a getter and setter for this field.
EDIT Action:
In the editAction() of the workers TableGateway, i have to fill the normal form + the form collection for the IDs, so i need data from both tables.
$worker = $this -> getWorkerTable() -> getWorker($id);
$departments = $this -> getDepartmentWorkerTable() -> getDepartments($id);
$worker -> setDepartmentIds($departments);
What type must the departmentIDs field in the worker Model be, that i now can use
$form = new WorkerForm();
$form -> bind($worker);
?
Let's now asume, data is posted and validated (Validation is set with the getInputFilter() in my Model). I want to save all the data:
$this -> getWorkerTable() -> saveWorker($worker);
$this -> getDepartmentWorkerTable() -> saveDepartments($worker);
Is that the right way to do it?
I think if this would work, the ADDAction is not the problem.
So the main questions are:
How can i set up a Model to handle one to many relationships? How must the exchangeArray() function be written?
How can i fill up a Form with ElementCollections with the Data from the Many Realtionship Table?
I think the main problem is, how to use an entity filled by the TableGateway which additionally contains an extra field for the relationship.
Some valuable tips would be great!
Regards, Yuri

Related

Html.ValidationMessageFor( MyKey ) without being tiedto a model's attribute?

My model does not really represent what my form is posting. Example my Orgs Model which holds orgs helps me generate a treeview the users selects several nodes of the orgs tree and submits a form. The form posts an array[] or org ids.
(maybe i'm doing this all wrong, please let me know tried binding to models and that was confusing when dealing with trees grids etc and using partial views and ajax returning partial views and editorfor's etc.. the default model binding was useless)
anyways back to my point, since I want to validate if any orgs get selected:
if (SelectedOrgs == null) //array[]
{
ModelState.AddModelError("OrgsNotSelected",IValidationErrors.OrgsNotSelected);
}
my question is how do i retrieve this random key that i just made up from my view? my model and even my viewmodel do not have an array for the selection this is just the result of the post.
I'm not sure what to do in the view to get the value for "OrgsNotSelected".
Thank you!
Bilal
If you were doing normal submit actions to your controller, you would need to use the ValidationSummary to display errors that are not attached to a specific property.
As you are using Ajax, you would be better off returning a json result from your controller and you can define this so that it includes your errors in a format you can use in the success function to display your messages.

Adding related master/child entities (one to many) in breeze

I have a master file for a widget and there can be many versions of that widget that share the same widgetmaster. So there are widgetMasters and widgetVersions tables on the db.
widgetMaster ID is an identity integer field and has an icollection of widgetversions set.
Widgetversion has a foreign key pointing to the widgetMaster ID it belongs to.
I have a "create new widget" form on my site. This is loaded after a button is pressed and it uses "createEntity" to create blank entities for widgetMaster and widgetVersion. The idea is that the parent "widgetMaster" is created at the same time the version "001" is created. The master just contains the description and a few category fields. The version contains the specific fields relating to this version of the widget and there may be dozens of versions eventually.
The user fills in all the fields and presses "save".
At this point I validate the form fields and, if all is ok, move on to saving the entity via "datacontext.saveChanges()" This is done in the viewmodel for my "create new" form view.
This works fine when creating the widgetMaster, but I need to have more control of this process I think... I need to set the foreign key on the widgetVersion entity AFTER The id is created by "savechanges" but BEFORE it attempts to save the widgetVersion entity.
As "datacontext.saveChanges()" appears to be a one-stop shop I'm entirely baffled as to how I can save the widgetVersion entity with the newly-created ID from the widgetmaster I just saved.
Alrighty then. I can't say whether it's the best way of doing it, but here's how I accomplish it. Refer to this stackoverflow question for a bit more info: Breeze bug? It's trying to add related entity even though only the primary entity is specified in savechanges()
My viewmodel save method (on the form entry view that allows the user to populate the fields in the new entities) is now this:
var save = function () {
isSaving(true);
//1st save widgetMaster
return datacontext.saveChanges(new Array(newWidgetMaster())).then(function (saveResult) {
//set version entity to have master id
newWidgetVersion().widgetMasterID(newWidgetMaster().id());
return datacontext.saveChanges(new Array(newWidgetVersion())).fin(complete);
}).fail(function (e) {
//do something with the alert
});
function complete() {
isSaving(false);
}
};

searchable grid using knockout in mvc

I need solution for my problem on urgent basis, I am new with mvc, knockout please provide me sample code for my problem. any help will be highly appreciated.
suppose I have an observable array in my viewmodel i.e
var viewmodel = {
vendorproviders : ko.observablearray([])
}
where vendorproviders list consist of multiple attributes like id, name, country, address etc
I want to populate that array in my grid where each row will have a select button, when that button is clicked it should post the id to my controller action either by submitting or by ajax call.
Furthor more that grid should be searchable like if there is a separate text box, based on the value of text box grid should display matching providers else display all providers.
when user search for particular provider grid should populate from observable array instead of making call at server again and again to pupulate the observable array.
I would suggest starting here.
http://learn.knockoutjs.com/#/?tutorial=intro
What you are talking about is all the basic functionality of the tools you referenced.

Is it possible to name (or tag) FormStack fields with simple identifiers?

This question assumes familiarity with FormStack, a drag-and-drop WYSIWYG online form builder.
Background
A client is using FormStack to manage forms. Currently, form submissions are emailed, then manually entered into a database. Predictably, my task is to automate this process. This is easy enough using FormStack's WebHooks API: I can have form submissions sent to a URL, e.g. a PHP script, and happily parse away.
Question
Is it possible to name (or tag) FormStack fields with simple identifiers?
The client needs to be able to customize the form such that multiple fields may feed into the same database column.* FormStack however, as far as I can tell, provides only a way to specify a field label, e.g. Which of these trips interest you?, not a programmer-friendly identifier, e.g. Trip. My script would have to string-compare labels (which, due to their length, are more prone to typos) to determine what to do. What are some sensible workarounds to this problem?
Clarifications*
The reason there can exist multiple fields that feed into the same database column, is that the client uses conditional fields. For example, one field might ask, Where are you studying abroad? If the user selects "Europe", a conditional field might appear, asking Which of these trips interest you?, with choices pertaining to Europe. If the user selects "Africa" however, a similar field might appear, e.g. Which of these trips interest you?, but with choices pertaining to Africa. In FormStack, these are actually two distinct fields. However, as you can imagine, the values belong in the same database column, Trip.
I have settled on a hack for now. FormStack allows HTML markup in labels, e.g. Which of these trips interest you? <!--Trip-->. The client is willing to "tag" fields in this way.
Here's a snippet of the code that parses such tags, in case it might help someone else:
require_once 'Formstack.php';
$formstack = new Formstack($apiKey);
$form = $formstack->form($_POST['FormID']);
$taggedFields = array();
foreach ($form['fields'] as $field)
{
if (preg_match('/<!--\s*([0-9A-Za-z]+)\s*-->/',
$field['label'],
$matches))
{
$taggedFields[$matches[1]] = $_POST[$field['id']];
}
}
In fact, I've had to make it a little bit more sophisticated. Some FormStack field-types serialize input (in a horrific way). For example, FormStack's Name field-type takes multiple fields (prefix, first, middle, last, initial, suffix), and concatenates the results into a string:
'first = Andrew
initial = W
last = Cheong'
To handle this, I've written my code to handle such syntax in labels as Tell us your name! <!--FirstName=first--> <!--LastName=last--> <!--MiddleInitial=initial-->
The code follows.
require_once 'Formstack.php';
$formstack = new Formstack($apiKey);
$form = $formstack->form($_POST['FormID']);
$taggedFields = array();
foreach ($form['fields'] as $field)
{
if (preg_match_all('/<!--\s*([0-9A-Za-z]+)\s*(?:=\s*(\w+))?-->/',
$field['label'],
$matches,
PREG_SET_ORDER))
{
foreach ($matches as $captures)
{
if (count($captures) == 3 &&
preg_match('/(?:^|\n|\r)'.$captures[2].' = ([^\n\r]+)/',
$_POST[$field['id']],
$subcaptures))
{
$taggedFields[$captures[1]] = $subcaptures[1];
}
else
{
$taggedFields[$captures[1]] = $_POST[$field['id']];
}
}
}
}
Hopefully, FormStack will soon add a native way to name or tag fields!

Apostrophe CMS: Engine Creation

I'm attempting to create a Product Engine for Apostrophe. I'm having trouble extending the Page Settings form, at the moment I want to add a simple textarea to add a synopsis to the page - eventually I want to add Product settings but I need to get the basics working first.
I've created a form and a settings partial, it's displaying fine and saving the data (with the help of a little hack - might not be correct). The trouble I'm having is when you edit a page the data is not being pulled back in to the form. To be honest I probably doing something fundamentally wrong but I lack experience in Symfony.
My table schema
ccProduct:
tableName: cc_product
actAs:
Timestampable: ~
columns:
page_id:
type: integer
notnull: true
synopsis:
type: text
relations:
Page:
class: aPage
local: page_id
foreign: id
type: one
onDelete: CASCADE
My form ccProductEngineForm.class.php
class ccProductEngineForm extends ccProductForm
{
public function __construct($object = null, $options = array(), $CSRFSecret = null)
{
// when editing the page the values are not show, this is an attempt to get it to work - it still doesn't :(
$page_id = sfContext::getInstance()->getRequest()->getParameter('id');
sfContext::getInstance()->getRequest()->setParameter('page_id', $page_id);
$ccProduct = Doctrine::getTable('ccProduct')->findOneByPageId($page_id);
if ($ccProduct) {
sfContext::getInstance()->getRequest()->setParameter('id', $ccProduct->getId());
}
// aPageForm object is passed in
parent::__construct(null, $options, $CSRFSecret); // construct normally
//$this->mergeForm(new aPageForm($object, $options, $CSRFSecret)); // merge the aPageForm - Nope, ignore it!?
}
public function setup() {
parent::setup();
$this->useFields(array('synopsis'));
$this->widgetSchema->setNameFormat('enginesettings[%s]');
$this->widgetSchema->setFormFormatterName('aPageSettings');
}
protected function doSave($con = null)
{
// page_id is missing! possible bug? BaseaActions.class.php ~ 520
$this->values['page_id'] = sfContext::getInstance()->getRequest()->getParameter('enginesettings[pageid]');
parent::doSave($con);
}
}
Thanks in advance for any help
EDIT:
Thanks for your answer Tom, I'll try to add a little more detail.
I was aware that a page object is passed into the Engine, but I wasn't exactly sure what do to with it - see my confused line of code:
//$this->mergeForm(new aPageForm($object, $options, $CSRFSecret)); // merge the aPageForm - Nope, ignore it!?
To clarify my 'product' is a page that uses the ccProduct engine. I now want to add extra information to that page. Does that make sense? In your words..
Are you trying to actually create a unique product that has its sole "home" on a product engine page? That's what subclassing ccProductForm would do
Yes :)
EDIT 2:
Following Tom's first suggestion (Apostrophe CMS: Engine Creation) I was able to extend the aPage table with my extra fields and the Engine is now saving these.
However, the standard aPageTable::getPagesInfo function isn't returning the fields I saved. I assume I'll have to select these separately?
EDIT 3:
aPageTable::retrieveBySlug() will do the job :)
REVISITED
I decided to revisit this and try Tom's second approach..
The other approach (if for whatever reason you don't want extra columns in aPage) is to keep your ccProduct table and fetch the relevant one
I managed to get this working, my ccProductEngine form constructor now looks like this..
class ccProductEngineForm extends ccProductForm
{
public function __construct($aPage = null, $options = array(), $CSRFSecret = null)
{
$page_id = $aPage->getId();
if ($page_id) {
$product = Doctrine_Core::getTable('ccProduct')->findOneByPage_id($page_id);
if ($product) {
$ccProduct = $product;
} else {
$ccProduct = new ccProduct();
}
}
parent::__construct($ccProduct, $options, $CSRFSecret);
}
I hope this helps someone :)
The main thing to remember is that your engine settings form receives a page object as the first parameter to the constructor, and you need to associate whatever your data is with that page object. Usually the engine settings form is a subclass of aPageForm, but it does not have to be. All that is required is that you associate your product object(s) with the page object in some way. Depending on your goals you probably want a refClass that creates a one-to-many relationship between product engine pages and products, and a form for manipulating those relationships.
From your code it is difficult for me to guess what you really want to do. Are you trying to actually create a unique product that has its sole "home" on a product engine page? That's what subclassing ccProductForm would do. Or do you just want to select an existing product from the product table and associate it with each engine page? Or do you want to select one or more products and associate them with the engine page?
Stuffing things into the request object is definitely not the way to go (:
Please clarify and I can help you further.
Tom Boutell, Apostrophe senior developer
There are two approaches you could follow here.
One is to just extend the schema of aPage in your project level config/doctrine/schema.yml file:
aPage:
columns:
synopsis:
type: text
Now every aPage object has a synopsis column, which will be null by default, and your engine settings form can just manipulate that one column. Your engine form subclasses aPageForm. You don't need a constructor at all (the default one will suit you), and your configure() method is just:
$this->useFields(array('synopsis'));
Boom, you have a textarea for the synopsis that appears when the page type is set to this engine. You don't need a ccProduct table at all.
The other approach (if for whatever reason you don't want extra columns in aPage) is to keep your ccProduct table and fetch the relevant one. Your engine form class then does not subclass aPageForm, and your constructor has to use the page passed to it to fetch the related ccProduct object (using a Doctrine relation) or create a new one if there is none yet. This is not difficult, but so far it looks like you can keep it even simpler by just adding a column to aPage.

Resources