yii2 how ovverride integer fields into model? - overriding

I read here
https://github.com/yiisoft/yii2/issues/6797
it's possible override a field,
to transfer it into integer
i want force ID to number
i try to add into model this function:
public function fields()
{
$fields = parent::fields();
$fields = [
'ID' => 'integer',
'CODICE_SPEEDY',
'DESCRIZIONE',
'DESCRIZIONE_COMPLETATO',
];
return $fields;
}
but yii2 crash:
Getting unknown property: app\models\Interventi::integer
Can you help me?

Fields are used for renaming properties. They are not use for type casting. with this
'ID' => 'integer',
you are tying to make it possible to call $model->ID and that will show the table column 'integer'
When you say to force what do you mean? that is something your database should do, or the model validation. You want to force to get an integer from what / where?

public function fields()
{
$fields = parent::fields();
$fields = [
'ID' => function ($model) {
return intval($model->ID);
},
'CODICE_SPEEDY',
'DESCRIZIONE',
'DESCRIZIONE_COMPLETATO',
'SERVIZIO', 'ASSEGNAZIONE', 'PIANIFICAZIONE', 'ESECUZIONE', 'ATTESA_CLIENTE', 'COMPLETATO'
];
return $fields;
}

Related

Sortable Sonata Type Model in Admin

Did someone tried the tutorial about Sortable Sonata Type Model in Admin.
I've followed it step by step without missing anything (I'm pretty sure) but can't get a good result at the end.
Basically what I'm trying to do is : I have 3 entities, Article, Tag and ArticleTag (eq to User, Expectation and UserHasExpectation in the tutorial)
Everything seems good until the UserHasExpectationAdmin:
protected function configureFormFields(FormMapper $formMapper){
// ...
$formMapper
->add('userHasExpectations', 'sonata_type_model', array(
'label' => 'User\'s expectations',
'query' => $this->modelManager->createQuery('UserBundle\Entity\Expectation'),
'required' => false,
'multiple' => true,
'by_reference' => false,
'sortable' => true,
))
;
$formMapper->get('userHasExpectations')->addModelTransformer(new ExpectationDataTransformer($this->getSubject(), $this->modelManager));}
I think an attribute 'class' => 'UserBundle\Entity\Expectation' should be added to 'userHasExpectations' field else Symfony says that it's an invalid value.
Then the other problem is in the dataTransformer:
It launch me the error:
Attempted to call an undefined method named "create" of class "Main\CoreBundle\Form\DataTransformer\TagDataTransformer"
I think a use statement should be added but I don't know which one. More over, suppose I have the right use statement I don't realize what the writer is aiming to do, if it's creating UserHasExpectation records why don't he add a userHasExpectations->setUser($this->User) ???
Also I want to add after "vardumping" $this->Subject before :
$formMapper->get('userHasExpectations')->addModelTransformer(new ExpectationDataTransformer($this->getSubject(), $this->modelManager));
It seems to have a proper Entity Object with all fields on NULL values...
FINALLY SOLVED IT!
So, the code of the tutorial contains many...mistakes
In spite of trying to create 'userHasExpectation' in the DataTransformer we just return the object userHasExpectation in the reverse DataTransformer then we create our records in the postPersist and postUpdate of our Admin Class that way :
/**
* {#inheritdoc}
*/
public function postUpdate($object)
{
$position = 0;
$uniqId = $this->getUniqId();
$request = $this->getRequest()->get($uniqId);
$qb = $this->modelManager->createQuery('MainCoreBundle:ArticleTag', 'at');
$TagsToRemove = $qb->where('at.article = :article')
->setParameter('article', $object)
->getQuery()
->getResult();
foreach ($TagsToRemove as $Tag) {
$this->modelManager->delete($Tag);
}
foreach($request["article_tags"] as $tag)
{
$Tag = $this->modelManager->find('MainCoreBundle:Tag', $tag);
$article_tags = new ArticleTag;
$article_tags->setTag($Tag);
$article_tags->setArticle($object);
$article_tags->setPosition($position++);
$this->modelManager->create($article_tags);
}
}
/**
* {#inheritdoc}
*/
public function postPersist($object)
{
$position = 0;
$uniqId = $this->getUniqId();
$request = $this->getRequest()->get($uniqId);
foreach($request["article_tags"] as $tag)
{
$Tag = $this->modelManager->find('MainCoreBundle:Tag', $tag);
$article_tags = new ArticleTag;
$article_tags->setTag($Tag);
$article_tags->setArticle($object);
$article_tags->setPosition($position++);
$this->modelManager->create($article_tags);
}
}
Hope this will help Somebody who has the same trouble.
#Sonata-admin-team : I hope you will read this and have time to update the tutorial in question.
Thanks,
Epixilog
For Sonata 3 adding the class attribute 'class'=> 'UserBundle\Entity\Expectation' resolved the problem for me.

Filter ModelAdmin by many_many relation

I'm managing the DataObject class 'trainer' with ModelAdmin. A trainer has a many_many relation to my other class 'language'.
On my 'trainer' class I'm manipulating the 'searchableFields' function to display a ListboxField in the filters area.
public function searchableFields() {
$languagesField = ListboxField::create(
'Languages',
'Sprachen',
Language::get()->map()->toArray()
)->setMultiple(true);
return array (
'Languages' => array (
'filter' => 'ExactMatchFilter',
'title' => 'Sprachen',
'field' => $languagesField
)
);
}
That works like expected and shows me the wanted ListboxField. The Problem is, after selecting 1 or 2 or whatever languages and submitting the form, I'm receiving
[Warning] trim() expects parameter 1 to be string, array given
Is it possible here to filter with an many_many relation? And if so, how? Would be great if someone could point me in the right direction.
Update:
Full Error Message: http://www.sspaste.com/paste/show/56589337eea35
Trainer Class: http://www.sspaste.com/paste/show/56589441428d0
You need to define that logic within a $searchable_fields parameter instead of the searchableFields() which actually constructs the searchable fields and logic.
PHP would be likely to throw an error if you go doing fancy form stuff within the array itself, so farm that form field off to a separate method in the same DataObject and simply call upon it.
See my example, I hope it helps.
/* Define this DataObjects searchable Fields */
private static $searchable_fields = array(
'Languages' => array (
'filter' => 'ExactMatchFilter',
'title' => 'Sprachen',
'field' => self::languagesField()
)
);
/* Return the searchable field for Languages */
public function languagesField() {
return ListboxField::create(
'Languages',
'Sprachen',
Language::get()->map()->toArray()
)->setMultiple(true);
}
Yes, it's possible. You just need to override two methods - one in Trainer data object and one in TrainerModelAdmin. First one will make a field, second one will do filtering.
Trainer Data Object:
public function scaffoldSearchFields($_params = null)
{
$fields = parent::scaffoldSearchFields($_params);
// get values from query, if set
$query = Controller::curr()->request->getVar('q');
$value = !empty($query['Languages']) && !empty($query['Languages']) ? $query['Languages'] : array();
// create a field with options and values
$lang = ListboxField::create("Languages", "Sprachen", Language::get()->map()->toArray(), $value, null, true);
// push it to field list
$fields->push($lang);
return $fields;
}
Trainer Model Admin
public function getList()
{
$list = parent::getList();
// check if managed model is right and is query set
$query = $this->request->getVar('q');
if ($this->modelClass === "Trainer" && !empty($query['Languages']) && !empty($query['Languages']))
{
// cast all values to integer, just to be sure
$ids = array();
foreach ($query['Languages'] as $lang)
{
$ids[] = (int)$lang;
}
// make a condition for query
$langs = join(",", $ids);
// run the query and take only trainer IDs
$trainers = DB::query("SELECT * FROM Trainer_Languages WHERE LanguageID IN ({$langs})")->column("TrainerID");
// filter query on those IDs and return it
return $list->filter("ID", $trainers);
}
return $list;
}

Customise exported CSV content of Sonata Admin bundle

I'm new to sonata admin bundle. Now, I am try to export a csv file with something like: 'customer.phone', 'order.total'... but when I opened the csv file, in the field 'order.total' is only '99.99', I would like it to export as 'AUD $99.99', anyone know how I can achieve it? Thank a lot! The code is here:
public function getExportFields() {
return array('id','customer.First_name','customer.Last_name',
'customer.contact','total_amount'
);
}
You need to define method getTotalAmountFormated in your Order class, and make it return string that you need. Then add totalAmountFormated (or total_amount_formated, I think both should work) in array returned from getExportFields
public function getExportFields() {
return array('id','customer.First_name','customer.Last_name',
'customer.contact','totalAmountFormated'
);
}
Just to add, you can customize the header of each column like such:
public function getExportFields() {
return array(
'Id' => 'id',
'Customer First Name' => 'customer.First_name',
'Customer Last Name' => 'customer.Last_name',
'Customer Contact' => 'customer.contact',
'Total Amount' => 'totalAmountFormated'
);
}

PHPUnit: How to check that an array has an object with a specific attribute?

Say we have the following array, $myArray, to check as per var_export:
array (
0 => AnObject::__set_state(array(
'id' => 10,
'name' => 'foo'
)),
1 => AnObject::__set_state(array(
'id' => 23,
'name' => 'bar'
)),
2 => AnObject::__set_state(array(
'id' => 55,
'name' => 'baz'
)),
)
The assertion should pass if this array contains an AnObject which has a name of 'bar'.
I know that if I knew the position of the AnObject value, I could use:
$this->assertAttributeSame('bar', 'name', $myArray[1]);
Is there some way to use $this->assertThat(), or another type of contains to check the entire array and return true of one of the objects has the attribute that matches?
There is no such built-in assertion and I cannot think of any possibility to combine them to get the expected result.
What I recommend you - is to create a helper method that accepts an array and does the necessary check in a loop.
Other solution is to create completely new assertion just for this case, but I think it is an overkill for this task ;-)
Expanding on the answer provided by zerkms, below is how I approached this exact task:
PHPUnit_Framework_Assert::assertTrue($this->assertArrayContainsSameObject($yourArray, $theObjectToCheck));
To check an array contains an object with the same attributes and values (i.e. not necessarily referencing the same instance):
private function assertArrayContainsSameObject($theArray, $theObject)
{
foreach($theArray as $arrayItem) {
if($arrayItem == $theObject) {
return true;
}
}
return false;
}
To check for the same reference, simply change == to ===.
To solve the original poster's question:
PHPUnit_Framework_Assert::assertTrue($this->assertArrayContainsSameObjectWithValue($yourArray, 'name', 'bar'));
private function assertArrayContainsSameObjectWithValue($theArray, $attribute, $value)
{
foreach($theArray as $arrayItem) {
if($arrayItem->$attribute == $value) {
return true;
}
}
return false;
}

get vocabulary id by name

I can retrieve a vocabulary id directly from DB,
but is there a built in function for this?
for example:
i have a vocabulary called "listing",
i need that built in function takes "listing" as function argument, and return
a vid.
i am using drupal 6
I have a function for this, well almost..
/**
* This function will return a vocabulary object which matches the
* given name. Will return null if no such vocabulary exists.
*
* #param String $vocabulary_name
* This is the name of the section which is required
* #return Object
* This is the vocabulary object with the name
* or null if no such vocabulary exists
*/
function mymodule_get_vocabulary_by_name($vocabulary_name) {
$vocabs = taxonomy_get_vocabularies(NULL);
foreach ($vocabs as $vocab_object) {
if ($vocab_object->name == $vocabulary_name) {
return $vocab_object;
}
}
return NULL;
}
If you want the vid just get the vid property of the returned object and.
$vocab_object = mymodule_get_vocabulary_by_name("listing");
$my_vid = $vocab_object->vid;
Henriks point about storing it in a variable is very valid as the above code you won't want to be running on every request.
Edit
Also worth noting that in Drupal 7 you can use taxonomy_vocabulary_get_names() which makes this a little easier.
For Drupal 7 if you know the vocabulary machine name this is the way:
$vid = taxonomy_vocabulary_machine_name_load('your_vocabulary_name')->vid;
If you know only the Real name of vocabulary, you can use this function:
function _get_vocabulary_by_name($vocabulary_name) {
// Get vocabulary by vocabulary name.
$query = db_select('taxonomy_vocabulary', 'tv');
$query->fields('tv', [
'machine_name',
'vid',
]);
$query->condition('tv.name', $vocabulary_name, '=');
$vocabulary = $query->execute()->fetchObject();
return $vocabulary;
}
There is no built in function for this, afaik. You can roll your own by calling taxonomy_get_vocabularies() and search for your name in the resulting array, but this will do a database request on every call.
If you have a vocabulary that you often use from code, it might be easier/more effective to store the vid in a Drupal variable via variable_set() once and get it back via variable_get() (Many modules that create a vocabulary on install do it this way).
Edit: here is some sample code to do this on module install.
function mymodule_install() {
$ret = array();
$vocabulary = array(
'name' => t('myvocab'),
'multiple' => '1',
'required' => '0',
'hierarchy' => '1',
'relations' => '0',
'module' => 'mymodule',
'nodes' => array('article' => 1),
);
taxonomy_save_vocabulary($vocabulary);
$vid = $vocabulary['vid'];
variable_set('mymodule_myvocab', $vid);
return $ret
}
Should help.
function _my_module_vid($name) {
$names = taxonomy_vocabulary_get_names();
return $names[$name]->vid;
}
You know the node type to which the vocaulbary is associated. So just use taxonomy_get_vocabularies() and pass the node type as argument and you will get the details you want!
In Drupal 7 you could use:
$vocab_object = taxonomy_vocabulary_machine_name_load('vocabulary_name');
$my_vid = $vocab_object->vid;

Resources