CakePHP Tree Behaviour - Tree vs. Threaded in Controller - cakephp

Using the example in the Cookbook:
http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html
If you debug() this you get a flat array() of category(id => name)
Which is fine for most purposes, but if you wanted to create navigation based on this and had a slug for the URL, rather than the id, you're out of luck.
I'm aware that I could use $this->Category->find('threaded') in the controller to get all fields but it lacks the convenience of the flat list because I have to create a recursive function to generate the links using the slug, exactly what I'd hoped to avoid.
So what's the best way to get the additional Category.slug field I need in the view?

CakePHP's generateTreeList() method allows you to choose the keys and values that are returned. If, for example, you wished to return a list that had the slug as the key and the link text as the value, you could use the $keyPath and $valuePath parameters to make it so:
$data = $this->Category->generateTreeList(null, "{n}.Category.slug", "{n}.Category.text", '_');
debug($data); die;
might produce:
array(
["my-categories"] => "My Categories",
["fun"] => "_Fun",
["sport"] => "__Sport",
["surfing"] => "___Surfing",
["skating"] => "___Skating",
["friends"] => "__Friends",
["gerald"] => "___Gerald",
["gwendolyn"] => "___Gwendolyn",
["work"] => "_Work",
["trips"] => "__Trips",
["national"] => "___National",
["international"] => "___International",
["other-peoples-categories"] => "Other People's Categories",
["extreme-fishing"] => "_Extreme fishing"
)

Related

How to convert an json data in array using accessor method in Laravel?

I m new in Laravel 8. Please give any suggestion for the given below code or answer my question:
Given below is my code an example of what i really want to get:
my data is :
[{"column_1_1":"value_1_1","column_1_2":"value_1_2"},{"column_2_1":"value_2_1","column_2_2":"value_2_2"}]
and I want
Array
(
[0] => stdClass Object
(
[column_1_1] => value_1_1
[column_1_2] => value_1_2
)
[1] => stdClass Object
(
[column_2_1] => value_2_1
[column_2_2] => value_2_2
)
)
I am adding this answer as the other is showing only one way and is the least used... Mine is the Laravel way, the one you should always use and the most you are going to see all the time...
If you have a Model who's property is, for example, data, you don't have to overwrite how it is read by doing getDataAttribute and use json_decode...
If that property/field is a JSON/TEXT in your database (hence storing JSON), you just have to cast it.
class YourModel extends Model
{
protected $casts = [
'data' => 'array'
];
}
So later you can do:
foreach ($model->data as $item) {
...
}
And store info in it like:
$array = ['products' => ['item1', 'item2'], 'quantity' => 2];
$model->data = $array;
And it will get saved on the database like {products: ["item1", "item2"], quantity: 2}
Bro you just need to add the accessor method in your model like:
Guess you have xyz column in your database
public function getXyzAttribute($xyz)
{
return json_decode($xyz);
}
It will directly return the array.
Hope it worked. :)

Detect change in date field suitecrm

I'm trying to detect when the date has changed inside a regular "date" field
Have seen a few forum posts about how this can be done with text fields and dropdown, but it doesn't work for date fields.
Also tried:
'displayParams' =>
array (
'javascript' => 'onchange="checkStatusOption(this)"',
),
In the editviewdefs.php, but it didn't work (works for text fields though)
The closest I've got it actually just tracking every click on on the screen and then checking the before and after state of the date field, but it's obviously not a very elegant solution
Here's the code from the extended editview
function display() {
parent::display();
$js = <<<EOT
<script type="text/javascript" language="JavaScript">
// Capture initial state
calendar_before = document.getElementById("contract_date_c").value;
// Wait for any click to take place anywhere on the screen
$(document).click(function() {
// Capture state after we clicked somewhere
calendar_after = document.getElementById("contract_date_c").value;
// Compare the before and after
if(calendar_before != calendar_after) {
// Change detected
alert("Something's changed eh?" + calendar_before +" "+ calendar_after);
}
// Set the new state of the before_calendar
calendar_before = document.getElementById("contract_date_c").value;
});
}
</script>
EOT;
// now I output the javascript
echo $js;
}
UPDATE:
I also tried the suggested solution
1) Created a file custom/modules/un_inventory/contract_date_c_change.js and put the following inside:
function yourCustomFunction(formElement){
console.log(formElement);
}
2) Included a reference to that file in the metadata file (made sure it loads it):
array (
'file' => 'custom/modules/un_inventory/contract_date_c_change.js',
),
3) Attached the updateCallback to the field:
array (
'name' => 'contract_date_c',
'label' => 'LBL_CONTRACT_DATE',
// Checks if this field got filled up and shows hidden form field
'displayParams' =>
array (
'updateCallback' => 'yourCustomFunction();',
),
),
But nothing happens when I change that date field
Check this out (just tested in SuiteCRM 7.11) for a datetime field, for other fields take a look at this answer to another SO question
First, include your custom JS in the editviewdefs.php (example for Accounts module)
'includes' =>
array (
0 =>
array (
'file' => 'modules/Accounts/Account.js',
'file' => 'custom/modules/Accounts/myCustomFile.js',
),
),
Create the custom JS file custom/modules/Accounts/myCustomFile.js .
function yourCustomFunction(formElement){
console.log(formElement);
}
Then update the field you want to monitor for changes (contractsigned_c in the example) using the following code in the editviewdefs.php:
array (
'name' => 'contractsigned_c',
'label' => 'LBL_CONTRACTSIGNED',
'displayParams' =>
array (
'updateCallback' => 'yourCustomFunction(this)',
),
),
Now do a Repair and Rebuild inside the Admin/Repair section and voilà it should work :)
You can add the JS function on the function display() if you want, its the samething, the function will be called right after native combo update. It will look like this combo_contractsigned_c.update(); yourCustomFunction(this)
use this . it is working
YAHOO.util.Event.addListener('your_field_name', 'change', function(){
// your code here
});

How to retrieve a value from a query string that stems from a <select> form input?

I simply need to get the selected value from a query string. Currently I just get the index of the list and not the actual value. I cant use Post as I need the value for a pagination search.
I dont get an error but just the number of the index of the list returned. The answer must be simple and I have tried many combinations. I have tried ['Student'] in front of the query as well.
I just couldnt see the answer in the docs and previous posts on this topic didnt work for me either. I am stuck.
The query string has a number {url}students/myindex5?address_suburb=1 //suburb should be a string and it appears as a string in the drop down list
public function myindex5() {
$this->set('filterSuburb', $this->Student->find('list', array(
'fields' => array('Student.address_suburb')
)));
$havefilter = false;
$tsub = $this->request->query['address_suburb'];
// $tsub = array_combine($tsub, $tsub);//didnt work
debug($tsub); //I just get the index value of the list
if (!empty($tsub)) {
// ...
View
echo $this->Form->create('Student', array(
'type' => 'get',
'url' => array('controller' => 'students', 'action' => 'myindex5')
));
echo $this->Form->input('address_suburb', array(
'options' => $filterSuburb,
'empty' => '(choose one)')
);
echo $this->Form->end('Search');
For reference
how to get the selected values from a drop down list in cake php
http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html
When you use a select input, the value send is the value of the option tag:
<select>
<option value="1">Option 1</option>
</select>
If I choose "Option 1", you will get 1, not "Option 1".
If you want to change the value attribute, you need to set something else to the FormHelper::input method options parameter, something like:
array(
'value1' => 'Text 1',
/** etc. **/
);
If you want your value to be ids of Student, just change your find call to:
$this->set('filterSuburb', $this->Student->find('list', array(
'fields' => array('Student.id', 'Student.address_suburb')
)));
If you look at find('list') documentation, you'll see this:
When calling find('list'), the fields passed are used to determine what should be used as the array key and value, and optionally what to group the results by.
So passing Student.id and Student.address_suburb will output a select with Student.id as option value and Student.address_suburb as option text.
If you want something else than Student.id, just change it in the find('list') call, you can even change it to have option like <option value="redcliff">redcliff</option> (same value and text), by doing:
$this->set('filterSuburb', $this->Student->find('list', array(
'fields' => array('Student.address_suburb', 'Student.address_suburb')
)));

Drupal 7 How to use render element instead of variables in hook_theme()

I'm having trouble understanding the 'render element' key purpose when implementing hook_theme().
function personal_news_theme($existing, $type, $theme, $path) {
return array(
'teaser_list_by_user' => array(
'render element' => 'element',
),
);
}
I did my research and the way I understand it, the render element key is use when creating a theme function to modify another theme function's output ONLY! And there's no way to use it as to implement a new theme function.
If I'm wrong, how can I use it instead of variables?
In the example below, I created a new theme function using a render element.
/**
* Implements hook_theme().
*/
function rojo_theme() {
$items = array(
'rojo_content' => array(
'render element' => 'element',
),
);
return $items;
}
/**
* Theme function.
*/
function theme_rojo_content($vars) {
return '<pre>' . print_r($vars, TRUE) . '</pre>';
}
/**
* Render function.
*/
function rojo_render() {
$build = array(
'#theme' => 'rojo_content',
'#module' => 'rojo',
'content' => 'Page content for the render function',
'list' => array('one', 'two'),
'tag' => 'div',
);
return render($build);
}
This will print the output of the $vars passed into the theme function. From here, you should be able to see what is going on. The #theme property will be called with theme() and passed the $build array during the render() process. Notice I added #module property and Drupal added the #printed / #children properties.
This is purely an example to demonstrate the creation of a new theme function using render element and argument passing. I hope this helps somebody out.
Array
(
[element] => Array
(
[#theme] => rojo_content
[#module] => rojo
[content] => Page content for the render function
[list] => Array
(
[0] => one
[1] => two
)
[tag] => div
[#printed] =>
[#children] =>
)
)
If your theme function returns an array - you can specify which key of the array to use for rendering.
render element: The name of the renderable element or element tree to pass to the theme function. This name is used as the name of the variable that holds the renderable element or tree in preprocess and process functions.
(Source: hook_theme)

cakephp string field used to create checkboxes

I'm using cakephp 2.1 and let me tell you: I just love it.
In my form I have a field that can have multiple answers (checkboxes).
I don't want to create a database field for each option, nor use an HABTM.
Update:
Since I needed several sets of flags, I went the $hasAndBelongsToMany way. (allos to add new flags without fixing the code but editing a table (eg. via phpmyadmin).
1.- Made a table/model for each set of flags I need:
2.- In my main model, declared the relation to each:
var $hasAndBelongsToMany = array('Sample','Format','Report','Openend','Dataproce','Prefield');
3.- In my main Controller, populated an array per table to use:
$openends = $this->Project->Openend->find('list', array(
//'order' => 'name'
));
4.- And use the arrays in the view:
echo $this->Form->input('Dataproce', array('label' => false, 'type' => 'select', 'multiple' => 'checkbox', 'size' => 2));
=== Old question starts here; correct answer worked for only one set of flags ===
I'd like to save a string and use it to to magically create a group of checkboxes that belong to a single data field.
I'm in the middle of it already.
my view:
echo $this->Form->input('pr_reports', array('type' => 'select',
'multiple' => 'checkbox',
'options' => array('0' => 'Interrnal',
'1' => 'Disposition',
'2' => 'Web Disposition',
'3' => 'Marginal',
'4' => 'Custom')))
my controller add method, before saving
// Used serialize() to convert the array to string
$dataString = serialize($this->request->data['Project']['pr_reports']);
$this->request->data['Project']['pr_reports'] = $dataString;
The string is being saved allright (encoded it seems but is ok: a:5:{i:0;s)
My question is how would I go when editing the record in order for the checkboxes to check themselves accordingly? That is, where do I unserialize() the string field and handle this on the edit view?
Is there a better way for this?
Thank you very much for any help.
Carlos García
==== After solution, troubles to have more than one field with a different set of flags
data is only saved for one field, ignoring the other ====
Hello;
For one field on the table it works just fine as noted;
I'm having a hard time using another field (separate set of flags).
It seems only one behaviour is attached; I was wondering if I should attach them differently.
my fields:
pr_data_format` tinyint(3) unsigned NOT NULL,
pr_report_format` tinyint(3) unsigned NOT NULL,
my controller
$this->Project->Behaviors->attach('Bitmasked', array('mappedField'=>'pr_data_formats', 'bits'=>'Project::pr_data_formats'));
$this->Project->Behaviors->attach('Bitmasked', array('mappedField'=>'pr_report_formats', 'bits'=>'Project::pr_report_formats'));
my model
const STATUS_ASCII = 1;
const STATUS_SPSS = 2;
const STATUS_EXCEL = 4;
const STATUS_CUSTOM = 8;
public static function pr_data_formats($value = null) {
$options = array(
self::STATUS_ASCII => __('ASCIId'),
self::STATUS_SPSS => __('SPSSBd'),
self::STATUS_EXCEL => __('Exceld'),
self::STATUS_CUSTOM => __('Customd'),
);
return parent::enum($value, $options);
}
const REP_ASCII = 1;
const REP_SPSS = 2;
const REP_EXCEL = 4;
const REP_CUSTOM = 8;
public static function pr_report_formats($value = null) {
$options = array(
self::REP_ASCII => __('ASCIIr'),
self::REP_SPSS => __('SPSSBr'),
self::REP_EXCEL => __('Excelr'),
self::REP_CUSTOM => __('Customr'),
);
return parent::enum($value, $options);
}
my view
echo $this->Form->input('pr_data_formats', array('options' => Project::pr_data_formats(), 'multiple' => 'checkbox'));
echo $this->Form->input('pr_report_formats', array('options' => Project::pr_report_formats(), 'multiple' => 'checkbox'));
Just can't figure it out, tried:
$this->Project->Behaviors->attach('Bitmasked', array('mappedField'=>'pr_report_formats', 'bits'=>'Project::pr_report_formats'), array('mappedField'=>'pr_data_formats', 'bits'=>'Project::pr_data_formats'));
but no use, only one field is updated.
Can you help? We'll use some 4 or 5 flagsets.
Thank you so much.
Carlos
a proper way to do this is using a behavior. this keeps your model clean and can be applied to several models simply by putting this in your model:
public $actsAs = array('MyBehavior');
now, for serializing I use my Jsonable Behavior:
http://www.dereuromark.de/2011/07/05/introducing-two-cakephp-behaviors/
it basically makes your input array a storable string on save and the string back to an array on read. you could easily adjust this to your needs.
BUT for what you want to do with multiple checkboxes there is even a better thing - bitmasks. I developed a so called Bitmasked behavior - you would need to use 1,2,4,8,... but other than that its the same thing:
http://www.dereuromark.de/2012/02/26/bitmasked-using-bitmasks-in-cakephp/
I use it exactly for the same thing.

Resources