I am new to Symfony2 and building an app based on an existing legacy MySQL schema. I've become familiar with all the Intro docs (The Book etc) but still needing to inderstand some higher level concepts of how to properly use the framework. Trying to get my head around the concept of an entity in terms of how I normally would go about writing SQL queries. I've used the CLI to generate entities for all my existing tables. As an example ... there is a Clients and a Titles entity already. Titles are 'owned by' Clients and the core Symfony annotations have mapped them correctly.
So, given a titles table with many columns of values but only one titles.client_id ... say I want to create a form action in the ClientsController (clients.yml route: /clients/{id}/add_title) that for the given client id in the url will allow the user to enter a title name and have it save a new record into titles with only the titles.name & titles.client_id values ... very simple really.
My question is ... in defining this very simple query (in normal SQL)
INSERT INTO (titles) VALUES (name, client_id)
DO I need to create another entity for titles JUST to work with those 2 specific values?
OR
What is the ideal way to use part of an entity for a specific repository ... in this case just a subset of the titles table (name & client_id)?
Here is the Action method in my Clients Controller:
//use Entity & Form namespaces for BOTH tables;
public function addTitleAction(Request $request)
{
$client_entity = new Clients;
$titles_entity = new Titles;
// generate simple 2 input form with Form\TitlesType
return etc ...
}
You may be able to tell, I also need to figure out how to work with the Form classes but my basic question here is how to generate simple queries from larger Entities and how to call from the Controllers of another Entity/Table Controller. Thx for your help.
To wrap your head around the new concepts, think of an entity as a row returned from your table. Think of a repository as your queries on the table. So you should have a Title entity (not Titles).
INSERT INTO (titles) VALUES (name, client_id)
DO I need to create another entity for titles JUST to work with those
2 specific values?
You'll want to create a new object when creating a new record (think of the new object as a new record that you then save), along the lines of:
$title = new Title();
$title->setClient($client);
$em->persist($title);
Related
I have a little problem and I can't solve it.
I have a controller, which accept 4 variables using AJAX and I need to insert data in first table and then get ID value from first table and insert it to 2 with 2 additional parameters.
So, my app structure is:
1) Keywords Table with fields keywordId, KeywordVal and Page ID (getted from AJAX)
2) Translations table with fields keywordID (get from Keywords.keywordId), langCode and translation (AJAX)
3) Controller which get data from ajax, proceed it and insert into table.
So, my question is next: how can I configure my EventListener? This listener must be run after flush() method and insret data into Translations table.
Why not you create it all in the controller, because to do all in a EventListener you must in some way to pass the values to the listener.
If you do it in your controller yo can to persist first the first entity with the needed parameters and in after persist the second entity related to de previusly created entity.
Something like:
$em = $this->getDoctrine()->getManager();
$keyword = new Keyword($param1, $param2);
$em->persist($keyword);
$keywordTranslation = new KeywordTranslation($keyword, $param3, $param4);
$em->persist($keywordTranslation);
$em->flush();
I think is so much easy to do
What you seem to need is a Doctrine Listener. Here is the documentation to create a Doctrine listener. You might want to use the preFlush event in my opinion. Be careful, this event is fired for every flush, not only for Keywords, so you've got to check first it is a Keyword before creating Translations.
EDIT: Nevermind, just noticed this does not answer to your question. However, I feel your model could be improved, because you should not have to flush several times to insert a single set of data. Theoretically, you should have a OneToMany relationship between Keywords and Translations, and Doctrine would manage alone to link the two entities with their id at insertion.
I have three tables:
banners (id, url, img)
banner_on_position (banner, position, loading)
banner_positions (id, name)
When I run the doctrine:mapping:import, doctrine creates only two tables. Banners and BannerPositions. Auto-generation doesn't create an own entity for the banner_on_position table.
Is there a way to access the loading attribute with querybuilder?
OR
How to tell doctrine to create own entity?
Until now I had no problems with auto generation of entities and it was very comfortable. I hope I don't have to create to code manually.
You have to create code manually in that case. If your many_to_many table have some extra properties then you need to use that trick: Both side need to use oneToMany relation for banner_on_position and banner_on_position need to have manyToOne to both tables.
Check how we did it in similar case (ContainerWidget have link to container, widget and custom parameter - position). https://github.com/superdesk/web-publisher/blob/master/src/SWP/Bundle/TemplateEngineBundle/Resources/config/doctrine/ContainerWidget.orm.yml
Here is also really good question and answers on stackoverflow: Doctrine2: Best way to handle many-to-many with extra columns in reference table
I've been thrown a 'can it be done in Doctrine' type question by my supervisor, and while I've been able to do some of it with external 'Mapper' objects, I'm wondering if there might be any way to fake-out the ORM to do it within Doctrine itself.
Essentially, the thing we are interested in doing is cutting down database clutter. We have a number of tables containing distinct properties or item sets in various categories and then a whole bunch of link-tables tying them to other properties or item sets. For example
We have a couple of tables such as 'Materials' and 'PaperTypes' which describe various Material and Paper options for our products. We then group these into 'MaterialCollections' and 'PaperFamilies' respectively. Then there has to be a one-to-many link table between MaterialCollection/Materials and PaperFamilies/PaperTypes respectively. Repeat these types of relationships a couple dozen more times and you see where our DB is starting to clutter up.
The link tables themselves are nothing more than a number of entries including multiple records with the PrimaryId from the parent table (collections/families) and unique-per-parent PrimaryId's from the sub-table (materials/papertypes). Link tables could be named something like MaterialCollectionsMaterials and PaperFamilyPaperTypes for example.
The idea was to get rid of this slew of link tables by using an abstract 3-table structure as follows:
Lists (consisting of a unique ListId, a ListName and a TypeId)
Types (consisting of a unique TypeId, a TypeName and an EntityName)
ListXref (consisting of ListIds and memberId which points to the primaryId from the Entity designated in the list type)
Multiple lists can exist for a given type, but the Xref pairs are unique. Different Types can also point to the same Entity. (e.g. there may be more than one type of Material list defined)
The 'ListName' would be the equivalent of the parent TableNames above and would allow for eliminating two of the tables in those relationships. So the records in 'MaterialCollections' would now instead be records in the List table of TypeName "MaterialCollection". The records that would have been in a link table (such as MaterialCollectionsMaterials) would now instead be pointed to from ListXRef.
As stated, I've gotten a basic mapper to make this work for rather basic list creation. But what I'm wondering is if there is any way to create Entities or things that behave like Doctrine Entities to establish the abstract relationships between a given list/listtype and the table referenced by EntityName and the corresponding memberIds?
In other words, it would be really nice if I had some means by which I could produce something that had the bulk of functionality of a Doctrine Entity which could be retrieved from the Service Manager or something like it that would behave (have similar properties/functions) like a Doctrine Entity.
I tried adding a wrapper object in the Entity tree that my mappers could try to retrieve which was basically an inherited version of the Xref entity with a few alias functions, but it can't be retrieved with the Entity manager.
Any help is appreciated.
P.S. While it is not a priority at the moment, longer term I also want to really throw a wrench into the works by trying to have some lists be capable of pointing back to the records produced by other lists. So, for example, a List "ProductXMaterials" of type "ProductMaterials" might point to some but not all results of "MaterialCollections". But I'll worry about this one later.
Well, Doctrine can't generate "pseudo entity classes" but you can.
First you need to hook into the Doctrine "loadClassMetadata" event.
Inside, you generate your entity classes and configuration from code on the fly and dump them into a specified directory (in the cache for example) maintaining the doctrine convention naming but with a custom namespace. For example : "AutoGeneratedNamespace\Entities"
So let's say you need to have a dynamic entity generated for an entity with name: "TmpUser", and you're using "yml" instead of annotations
<?php
namespace Example;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
class DoctrineEventListener
{
/**
* Invoked on Doctrine loadClassMetadata event
*
* #var Doctrine\ORM\Event\LoadClassMetadataEventArgs
**/
public function loadClassMetadata(LoadClassMetadataEventArgs $args)
{
$em = $args->getEntityManager();
$metadata = $args->getClassMetadata();
$factory = $em->getMetadataFactory();
$name = 'AutoGeneratedNamespace\Entities\TmpUser';
$tmpDirectory = '/path/to/cache/generated-classes';
// current class metadata is the same as the one we need. this means we already generated it
if($metadata->getName() == $name || $factory->hasClassMetadata($name)) {
return;
}
/**
Generate your class here and dump it into the cache directory with the name: TmpUser.php
(If you're using "yml" instead of annotations also create the TmpUser.orm.yml configuration and dump it in the same directory)
**/
// create a metadata for the newly created class
$metadata = $factory->newClassMetadataInstance($name);
// Register metadata so doctrine knows about it
$factory->setMetadataFor($name, $metadata);
}
}
Finally, you tell Doctrine to look for entities inside the cache directory as well using the configuration: (This is taken from the Symfony documentation so if you're not using Symfony it may vary a bit but you can find the precise configuration in the Doctrine docs)
doctrine:
orm:
mappings:
MyGeneratedEntitiesUniqueMappingsKey:
type: yml
dir: '/path/to/cache/generated-classes'
is_bundle: false
prefix: "AutoGeneratedNamespace\Entities"
alias: GeneratedEntities
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I need to develop a custom form generator using Laravel.
That means there will be a GUI to select and customise the forms like Registration Form or Booking Form.User should be able to add/edit/delete different form controls, define it as mandatory, etc.
Here I am little confused to handle this in back-end. What is the better way to achieve this?
Or how can I implement a database architecture to use some metadata table which can be used to handle multiple items like Wordpress and is there any built-in Laravel functionalities to handle these meta objects?
And how the insert/update/delete handled in this metadata approach?
Here an insert should have only one row in the meta table. Suppose at the time of user registration, without saving the firstname and lastname in separate rows in the meta table, it should use some objects like this in a single row.
a:3:{s:9:"firstname";s:10:"irshad.far";s:8:"lastname";s:0:"";s:5:"_meta";a:7:{s:2:"ip";s:10:"14.99.80.3";s:9:"confirmip";s:10:"14.99.80.3";s:11:"confirmtime";d:1407932201;s:8:"signupip";s:10:"14.99.80.3";s:10:"signuptime";d:1407932201;s:4:"lang";s:2:"en";s:4:"form";s:7:"unknown";}}
Handling a table of meta data is fairly straight forward using Laravel's Eloquent relations. Let's say you have a users table in your database that contains:
id email password created_at updated_at deleted_at
If you want to keep it simple and not add all sorts of extra data to your users table you could create a meta table and then a link table user_meta to relate the two.
But what if you also have a posts table (as with Wordpress) and your posts also need meta data? Instead of also creating a posts_meta table to link your posts to their meta, we can use Laravels Eloquent relations and create some Polymorphic Relations.
The Database
Here's our setup, along with our users table (above) we have a posts table which has the fields:
id title content created_at updated_at deleted_at
We also have our meta table that follows the guidelines for a polymorphic relation:
id name value metable_id metable_type
//int meta key meta value post/user id resource ie post/user
Using this we could add meta for a post or user to our meta table like this:
id name value metable_id metable_type
------------------------------------------------------
1 nickname Steve 1 User
2 author Steve O 1 Post
All we need to do to grab this info from the database is define the relations in our respective models.
The Models
So now we have our DB ready we need to setup our models (one model for User, one for Post and one for Meta) with our polymorphic relationship. Our User and Post models are both going to use the same function to relate to our Meta model:
User.php
========================================
class User extends Eloquent {
public function meta()
{
return $this->morphMany('Meta', 'metable');
}
}
Post.php
========================================
class Post extends Eloquent {
public function meta()
{
return $this->morphMany('Meta', 'metable');
}
}
Now we define the inverse of those relations in our meta model:
Meta.php
========================================
class Meta extends Eloquent {
public function metable()
{
return $this->morphTo();
}
}
That's it!
Getting the data
Now all you need to do to get at the meta data for a user or post is:
// Load in a post with an id of 1 and get all it's related meta
$post = Post::find(1);
$meta = $post->meta;
If we were to return the meta object we might see something like:
[{"id":2,"metable_id":1,"metable_type":"Post","name":"author","value":"Steve O"}]
Onwards!
From here you can create helper functions like this one that checks if the meta you're after exists in the results:
public function hasMeta($key)
{
$meta = $this->meta;
foreach ($meta as $item):
if( $item->name == $key ) return true;
endforeach;
return false;
}
// Use it like:
if($post->hasMeta('author')){
// Display author
}
Read more about Laravels Eloquent relationships in the docs here: http://laravel.com/docs/eloquent-relationships
I once did something similar, my approach was to build a mini DB engine where forms are like tables and data is rows:
A form which describes the structure and design of a form:
Form {
id,
title,
layout,
...
}
Fields of the form with types and validation rules
Field {
formId,
name,
type (String, Date, Image, Integer, Double, List, ...),
pattern (Regex validation maybe),
...
}
Inserted data in a form is a row belonging to that form
Row {
id,
formId,
}
Each row is a group of entries to fields of the corresponding form that can be validated following the predefined rules.
Entry {
rowId,
fieldId,
value
}
Type and rules can be regrouped in another object so you can have dynamic types that you can manage.
Lists can have another object that stores choices and type of list (multi-select, mono-select)
Metadataobjects itself would be saved in one table. But performance-wise I think those object should their own data tables.
Approach 1)
These types of forms needs to be predefined and linked to a specific controller. This must be either so that there is only one controller for each type of form like Registeration, and only one user defined metadataobject can be in use at either time. This controller's table parameter would be set to point to a database table created specifically for that metadataobject (or perhaps same table could be migrated according to metadata but then that table should be empty).
Or 2) every metadataobject should have it's own controller created which points to the object's data table.
In each approach routes needs to be created to point to the one controller of each type at use in each time.
One dilemma is how to manage revisions of those objects. Perhaps each object might have a running number postfix, and have their own controllers and data tables created (then it might be easier to migrate even populated tables [then user would be notified on front-end if his action would result in data loss, like for example with deleting a form data field]).
Another part of this project is to create an intelligent generator engine for assembling the HTML, CSS, and JS code according to a metadataobject. The generated code can be saved to reside in the same table as the objects themselves, and when used should be cached in the backend for rendering views.
Such metadataobject must itself have a clever format, so that it composes of predefined pieces of settings which will be converted to functionality by the form generator code.
I'm new to zend framework but have made my first steps with it successfully.
Until now I have created some Zend_Forms which are mapping single records of my model
to the form fields. I have handled the forms with form classes for each case.
This works all very well until now.
Now I have the situation that I have to asign features to a product. Features and products are parts of my application. Features are stored in my database in three tables. For each feature there is one record in the third table.
First is the feature group where the name of the feature group is saved. Every feature should be asigned to a feature group.
Second table is the features table. This table has an foreign key to the feature group and the name of the feature.
Third table is some kind of many-to-many relation which connects features to products. This table has an aditional field which contains an optional value (beside the two foreign keys) for this unique feature of the product.
For example: if the product has a weight of 4,78 kg the value "4,78" is stored in the third table and the label "weight of %s kg" is stored in the second table. The feature group could be something like "physical attributes" had is saved in the first table.
To cut a long story short:
My problem is how to handle the case that I have to create and edit multiple database records in one form. The plan is to have a form with many checkboxes for each for a feature whereby features are thematicaly grouped. Every checkbox should have an aditional text field to input optional values.
you could make a custom form class that extends Zend_Form and use that for you classes.
It could take in the construct instances of your models and construct the form inputs based on that models.
After form validation in your controller you can do
$values = $form->getValues();
and use that array to populate your models again
You can try creating subforms (Zend_Form_SubForm) inside your form class. This can separate fields for different tables. For edition, in your controller, when you pull all the data from the tree tables, you can populate subforms that correspond to the tables.
You can try to extend Zend_Form to create your own elements.
You will be able to write a class that connects to DB to get attributes (features & products).
Assuming you wrote My_Form_Element_Features & My_Form_Element_Products classes, you can do $features = new My_Form_Features(); and then use the base class methods like getValues(), populate(), etc.
You can take a look there to start :
http://framework.zend.com/manual/en/zend.form.elements.html
http://smartycode.com/extending/database-aware-select-elements/
--
To answer to your comment, you can use :
Zend_Form::setElementsBelongTo($array):
More information can be found at Zend_Form Advanced manual page.