Save safe string with createMultipleInsertCommand in yii project - php

How can i create safe string in yii, if i want to save multiple data?
This is my code:
$builder = Yii::app()->db->schema->commandBuilder;
$command = $builder->createMultipleInsertCommand('ewl_team_user', array(
array('user_name'=>'record1', 'user_desc'=>'leírás','team_id'=>1),
array('user_name'=>'record2', 'user_desc'=>'leírás','team_id'=>1),
array('user_name'=>'record3', 'user_desc'=>'leírás','team_id'=>1),
));
How can i use PDO params in this code?
Where should i write this part?
'params'=>array(':t'=>$data)

As I said in the comments, CDbCommandBuilder#createMultipleInsertCommand does not have any params argument. So the better way is to use CActiveRecord for saving data. First, you need to generate Model from table entity by Gii tools. If you are not familiar with Gii, refer this link. Then you can instantiate any number of objects, assign desired values to attributes, and finally save them.

Related

Enum list defined in a config yaml shared with multiple DataObjects

I have a few DataObjects that have a price tier associated to them. I could build this relationship in another DataObject however these tiers aren't going to change and I thought the best way was to define them in config.yml.
Ideally I'd like to define the DataObject like:
private static $db = array(
'Price' => 'Enum(array("Tier 1", "Tier 2"))'
)
However I can't think of a way to do this properly in SilverStripe having a single point of maintenance like a yaml. I also thought of just making it a Varchar and checking to make sure it's in the array before setting it however this won't allow things like populating lists and so forth via the enum (probably should call the config anyway I guess). I could also just write out the array however there would be at least two places for it which would be harder to maintain.
What's the best way to do what I'm trying to achieve which is several objects referencing an array set in a single place?
As shown by other answers, you can accomplish that in many ways. In a project I worked on for example I have something similar to the following:
// You need to put this into a _config.php. It copies the definition
// of DataObject2.Field2 into DataObject1.Field1.
$cfg = Config::inst();
$db = $cfg->get('DataObject1', 'db');
$db['Field1'] = $cfg->get('DataObject2', 'db')['Field2'];
$db = $config->update('DataObject1', 'db', $db);
But in my opinion the most elegant solution would be to define a new class, something like:
class MyEnum extends Enum
{
/**
* Enum values.
* #config
*/
private static $values;
public function __construct($name = null, $default = null)
{
parent::__construct($name, $this->config()->get('values'), $default);
}
}
After that you could define the values in plain YAML:
MyEnum:
values:
- First
- Second
...
You could apply the same db fields to multiple DataObjects by creating a DataExtension and applying it to all the DataObjects you want to add the fields to.
Of course you can add and overwrite $db keys in your config yml like:
MyDataObject:
db:
Price: "Enum('Tier 1,Tier 2,Tier 3', 'Tier 3')"
Just be sure to use the right syntax for Enum, it's easier with the old comma seperated syntax. In the example above "Tier 3" is default.
With defining it in yml it might break the order when you use scaffolding, haven't tested this yet.
You might also think about using a has_one relation to a simple dataobject instead. You can manage that in a ModelAdmin and which might be easier to translate later on. Also the QuickAddNew module is a good helper to add new DOs on the fly beneath a dropdown field.
I'm not 100% sure what you're asking, but if it's like the title and you're asking whether you can define Enum DB field values in YAML, yes - you can:
MyDataObject:
db:
Price: Enum("Tier 1,Tier 2")
What you can't do is add more values to the Enum from other configuration sources (including private statics), because the enum value is treated as a string literal rather than a YAML array.
If your question is "Can I define a common configuration for $db and use it for multiple DataObjects", then the YAML approach (config in general) won't work, since configuration values are tied to a class and are parsed earlier in the process than when DataObject::database_fields tells the DB schema what to do.

FOSElasticaBundle: Is it possible to change "query_builder_method" in controller?

According to FOSElasticaBundle documentation it is possible to configure application to use custom query builder method like this:
user:
persistence:
elastica_to_model_transformer:
query_builder_method: createSearchQueryBuilder
But is it possible to choose QB method live, e.g. in controller action?
I'd like to be able to control what's being fetched from DB while transforming Elastica results to Doctrine entities. E.g. sometimes I'll want to do eager fetch on some relations, but can't do that by default.
Since FOSElasticaBundle documentation is not very precise, I went through its code and found it impossible to control what query builder is used on controller level.
It is possible to change whole elastica_to_model_transformer to a custom service, but still it's statically defined in configuration. Maybe with some dirty solution it would be possible going this way, but I don't think it's worth it.
I decided to just not using this feature of FOSElasticaBundle. The main problem I had was that when you use fos_elastica.index instead of fos_elastica.finder or elastica repository (in order to get plain not transformed results Elastica\Resultset), there's no findPaginated method with returns Pagerfanta paginator object, which is very helpful in my case.
Fortunately although it's not mentioned in documentation it's possible to create the Pagerfanta this way too, but a little bit more manually.
Here's a code snippet:
//generate ElaticaQuery somehow.
$browseQuery = $browseData->getBrowseQuery();
$search = $this->container->get('fos_elastica.index.indexName.typName');
//create pagerfanta's adapter manually
$adapter = new \Pagerfanta\Adapter\ElasticaAdapterElasticaAdapter($search, $browseQuery);
// now you can create the paginator too.
$pager = new Pagerfanta($adapter);
//do some paging work on it...
$pager->setMaxPerPage($browseData->getPerPage());
try {
$pager->setCurrentPage($browseData->getPage());
} catch(OutOfRangeCurrentPageException $e) {
$pager->setCurrentPage(1);
}
//and get current page results.
/** #var Result[] $elasticaResults */
$elasticaResults = $pager->getCurrentPageResults();
// we have to grab ids manyally, but it's done the same way inside FOSElasticaBundle with previous approach
$ids = array();
foreach($elasticaResults as $elasticaResult) {
$ids[] = $elasticaResult->getId();
}
//use regular Doctrine's repository to fetch Entities any way you want.
$entities = $this->getDoctrine()->getRepository(MyEntity::class)->findByIdentifiers($ids);
This actually has a few advantages. In general it gives you back control over your data and doesn't tie ElasticSearch with Doctrine. Therefore you can resign on fetching data from Doctrine if you have all needed data in ElasticSearch (if they are read only data of course). This lets you optimize your application performance but reducing amount of SQL queries.
The code above may be wrapped with some kind of service in order to prevent making mess in controllers.

Clone entity and all related entities in CakePHP 3

In my CakePHP 3 app, I have a somewhat elaborate tree of entities that I need to clone and save.
The root of the structure is a Questionnaire, a Questionnaire hasMany Questions, each Question hasMany Fields, etc. (it goes deeper). Now I want the user to be able to define a new questionnaire by copying an old one. Then they can change whatever they need.
I can get a dump of what I need to copy by using $questionnaire->$this->Questionnaires->get($id) with the appropriate contain fields. Is there a clever way to save this as a bunch of new entities while preserving the data and the structure between them?
I think the best possible way would be following work flow:
Get object you want to clone
Go through the collection and remove all ID's
Convert to array and use that in $this->Questionnaires->newEntity($arrayData, ['associated' => ['Questions', '...']]);
Now save the new entity with all the related data you want to keep
AFAIK there's no "smarter" way of cloning an entity with associations in Cake 3 :-)
You could also use this plugin. You only have to configure the behavior and it gives you other functionalities, like setting default values or appending text to some field.
use EntityInteface toArray() to get all its fields:
$newEtity = $someTable->newEntity($oldEtity->toArray());
unset($newDevice->created);
unset($newDevice->id);
$someTable->save($newEntity);
$original = $this->Table->get($id)->toArray();
$copy = $this->Table->newEntity();
$copy = $this->Table->patchEntity($copy, $original);
unset($copy['id']);
// Unset or modify all others what you need
$this->Table->save($copy);
Work perfectly like this :)
Here's the way I did it when I needed something similar (adapted to the Questionnaire example):
// load the models
$this->loadModel('Questionnaire');
$this->loadModel('Questions');
// get the questions to questionnaire association - will need the PK name
$qAssoc = $this->{'Questions'}->associations()->get('Questionnaire');
// get the existing entity to copy
$existingEntity = $this->{'Questionnaire'}->get($id, [
'contain' => ['Questions'],
]);
// clean up the existing entity
$existingEntity->unsetProperty($this->{'Questionnaire'}->getPrimaryKey());
$existingEntity->unsetProperty('created');
$existingEntity->unsetProperty('modified');
// clean up the associated records of the existing entity
foreach ($existingEntity->questions as &$q) {
$q->unsetProperty($this->{'Questions'}->getPrimaryKey());
$q->unsetProperty($qAssoc->getForeignKey());
}
// create a new entity and patch it with source data
$newEntity = $this->{'Questionnaire'}->patchEntity(
$this->{'Questionnaire'}->newEntity(),
$existingEntity->toArray()
);
// save the new entity
$result = $this->{'Questionnaire'}->save($existingEntity);
References:
https://api.cakephp.org/3.0/class-Cake.ORM.Entity.html
https://api.cakephp.org/3.0/class-Cake.ORM.Table.html

Filtering with symfony2

Is there any open source (or example) code for Symfony2 which can filter certain model using multiple parameters? A good example of what I'm looking for can be seen on this Trulia web page.
http://www.trulia.com/for_sale/30000-1000000_price/10001_zip/
http://www.trulia.com/for_rent/Chicago,IL/#for_rent/Chicago,IL/0-500_price/wd,dw_amenities/sm_dogs_pets"
http://www.trulia.com/for_rent/Chicago,IL/#for_rent/Chicago,IL/400-500_price/wd,dw_amenities
http://www.trulia.com/for_rent/Chicago,IL/#for_rent/Chicago,IL/wd,dw_amenities"
http://www.trulia.com/for_rent/Chicago,IL/#for_rent/Chicago,IL/400p_price/dw,cs_amenities
http://www.trulia.com/for_rent/Chicago,IL/#for_rent/Chicago,IL/1p_beds/1p_baths/400p_price/dw,cs_amenities
Note how URL are build when clicking in the form, I guess is using one controller for all this routes, How is it done?.
I Don't think it will be redirecting all the possible routes to a specific controller, (shown below), maybe some sort of dynamic routing?
/**
* #Route("/for_rent/{state}/{beds}_beds/{bath}_bath/{mix_price}-{max_price}_price /{amenities_list}
* #Route("/for_rent/{state}/{mix_price}-{max_price}_price/{amenities_list}
* #Route("/for_rent/{state}/{bath}_bath/{mix_price}-{max_price}_price/{amenities_list}
* #Route("/for_rent/{state}/{mix_price}_price/{amenities_list}
* #Route("/for_rent/{state}/{beds}_beds/{bath}_bath/{amenities_list}
* ........
*/
public function filterAction($state, $beds, $bath, $min_price, $max_price ....)
{
....
}
Thanks.
For simple queries (i.e. where you don't need to have a data range, such as min-max value), you can use the entity repository to find entities by the request parameters given. Assuming that your entity is Acme\FooBundle\Entity\Bar:
$em = $this->getDoctrine()->getEntityManager();
$repo = $em->getRepository('AcmeFooBundle:Bar');
$criteria = array(
'state' => $state,
'beds' => $beds,
// and so on...
);
$data = $repo->findBy($criteria);
When building the $criteria array, you'll probably want some logic so that you only sort by criteria that have been provided, instead of all possible values. $data will then contain all entities that match the criteria.
For more complex queries, you'll want to look into DQL (and perhaps a custom repository) for finer-grained control of the entities that you're pulling out.
To construct your routes, i'm sure you had a look at the Routing page of the documentation, but did you notice that you can put requirements on routes? This page explains how to do it with annotations.
As for the filtering, I suppose DQL would be ok, but you can also write straight up SQL with Doctrine, and map the results of your query to one or more entities. This is described here. It may be more flexible than DQL.
csg, your solution is good (with #Route("/search/{q}) if you only need to use routing in "one-way". But what if you will need to print some price filter links on page accessible by url:
http://www.trulia.com/for_sale/30000-1000000_price/10001_zip/
In case of #Route("/search/{q} you will not be able to use route method url generate with params.
There is a great Bundle called LexikFormFilterBundle "lexik/form-filter-bundle": "~2.0" that helps you generate the complex DQL after the Filter form completed by the user.
I created a Bundle, that depends on it, that changes the types of a given FormType (like the one generated by SencioGeneratorBundle) So you can display the right FilterForm and then create the DQL after it (with Lexik).
You can install it with Composer, following this README.md
All it does is override the Doctrine Type Guesser, that suggests the required FormType for each Entity field, and replace the given Type by the proper LexikFormFilterType. For instance, replaces a simple NumberType by a filter_number which renders as two numbers, Max and Min interval boundaries.
private function createFilterForm($formType)
{
$adapter = $this->get('dd_form.form_adapter');
$form = $adapter->adaptForm(
$formType,
$this->generateUrl('document_search'),
array('fieldToRemove1', 'fieldToRemove2')
);
return $form;
}
Upon form Submit, you just give it to Lexik and run the generated query, as shown in my example.
public function searchAction(Request $request)
{
// $docType = new FormType/FQCN() could do too.
$docType = 'FormType/FQCN';
$filterForm = $this->createFilterForm($docType);
$filterForm->handleRequest($request);
$filterBuilder = $this->getDocRepo($docType)
->createQueryBuilder('e');
$this->get('lexik_form_filter.query_builder_updater')
->addFilterConditions($filterForm, $filterBuilder);
$entities = $filterBuilder->getQuery()->execute();
return array(
'entities' => $entities,
'filterForm' => $filterForm->createView(),
);
}

Getting database values into objects

The thing is that you have classes and then you have the database data. When you create an object how do you set the objects properties to contain the data in the database ?
I saw something like this and I'm wondering if this is really the best way to do it. I'm sure this is a fairly common issue, but I don't know what are the most accepted solutions on how to handle it.
In this example when the object is created you pass an id as a parameter and then you run a query to the database with the id and you assing the returned values to the object properties. I don't have much PHP experience and haven't seen this used much.
Is this an acceptable way to achieve this purpose ? Is there a better or more accepted way ?
public function __construct($id = null){
if($id != null){
$sql = "SELECT *
FROM users
WHERE user_id = $id";
$res = Db::returnRow($sql);
// $res contains an associative array with database columns and values
if($res){
$this->user_id = $res['user_id'];
$this->user_name = $res['user_name'];
//and so on...
}
}
}
Could somebody provide some sample code or pseudocode to illustrate what is the correct way to do this ?
It could be an acceptable way for a homework maybe. But architecturaly it is not.
Your class that is representing your business data (a user in your example) must be loosely coupled with your database access logic. In the end the PHP class acting as a user should not be aware that the data come from a database, a file or any other resource. Following that you will be able to reuse your user php class in other projects without having to change anything to it! If you have your data access logic inside it you are stuck.
Conclusion: I would suggest to read some resources on Design Pattern (in your situation take a look at DAO pattern) ;) Hint: the one from Head First series is extremely accessible and enjoyable.
You could create a function to do this for you automatically, by looping over the associative array's key/value pairs. Or you could look into using an ORM library.
Yes, you can semi-automate this by having a parent class all objects inherit from. On load, it queries, "SHOW FIELDS FROM [my tablename]" and populates an associative array with the names. If an id has been passed in, it looks for a valid object in that table with that id and assigns the values to the array.
Side note: don't pass your id directly into your query like that. Parametize the sql and wrap a function around any user input to sanitize it.
If it's mysql, you can just do:
$obj = mysql_fetch_object($query);
PDO the ability to use arbitrary classes as the target for a fetch, but beware that they assign the variable data before running the constructor:
$pdo->query($stmt, PDO::FETCH_CLASS, "MyClass", array('foo'=>'bar'));
...where the final parameter contains arguments for your class constructor.

Categories