Agile Toolkit 4.2 ... reference field type breaking with model - atk4

This is a simple re-write of the example code for a reference field:
class page_LoadResults extends Page {
function init(){
parent::init();
$p=$this;
$f=$p->add('Form');
$c=$p->add('Model_Season');
$f->addField('reference','Season')
->setValueList($c)
->validateNotNull()
->add('Icon',null,'after_field')
->set('arrows-left3')
->addStyle('cursor','pointer')
->js('click',$f->js()->reload())
;
When called I get an error message:
\atk4\lib\Form/Field.php:652 [2] htmlspecialchars() expects parameter 1 to be string, array given
Looking at the code, around line 648 in Field.php
foreach($this->getValueList() as $value=>$descr){
// Check if a separator is not needed identified with _separator<
$output.=
$this->getOption($value)
.htmlspecialchars($descr)
.$this->getTag('/option');
}
Is indeed apparently creating $descr as an array of ($value,descr)
IS this a bug or am I off base. Thanks.

use atk 4.2 syntax
<?php
class page_b extends Page {
function init(){
parent::init();
$p=$this;
$f=$p->add('Form');
$field = $f->addField('Dropdown','Season');
$field->setModel("a");
$field
->validateNotNull()
->add('Icon',null,'after_field')
->set('arrows-left3')
->addStyle('cursor','pointer')
->js('click',$f->js()->reload())
;
}
}
Pay attention to addField("Dropdown")
use setModel rather than setValueList($model);

Related

Indirect modification of overloaded element of Illuminate\Support\Collection has no effect

im quite new in laravel framework, and im from codeigniter.
I would like to add new key and value from database
static function m_get_promotion_banner(){
$query = DB::table("promotion_banner")
->select('promotion_banner_id','promotion_link','about_promotion')
->where('promotion_active','1')
->get();
if($query != null){
foreach ($query as $key => $row){
$query[$key]['promotion_image'] = URL::to('home/image/banner/'.$row['promotion_banner_id']);
}
}
return $query;
}
that code was just changed from codeigniter to laravel, since in codeigniter there are no problem in passing a new key and value in foreach statement
but when i tried it in laravel i got this following error :
Indirect modification of overloaded element of Illuminate\Support\Collection has no effect
at HandleExceptions->handleError(8, 'Indirect modification of overloaded element of Illuminate\Support\Collection has no effect', 'C:\xampp\htdocs\laravel-site\application\app\models\main\Main_home_m.php', 653, array('query' => object(Collection), 'row' => array('promotion_banner_id' => 1, 'promotion_link' => 'http://localhost/deal/home/voucher', 'about_promotion' => ''), 'key' => 0))
please guide me how to fix this
thank you (:
The result of a Laravel query will always be a Collection. To add a property to all the objects in this collection, you can use the map function.
$query = $query->map(function ($object) {
// Add the new property
$object->promotion_image = URL::to('home/image/banner/' . $object->promotion_banner_id);
// Return the new object
return $object;
});
Also, you can get and set the properties using actual object properties and not array keys. This makes the code much more readable in my opinion.
For others who needs a solution you can use jsonserialize method to modify the collection.
Such as:
$data = $data->jsonserialize();
//do your changes here now.
The problem is the get is returning a collection of stdObject
Instead of adding the new field to the result of your query, modify the model of what you are returning.
So, assuming you have a PromotionBanner.php model file in your app directory, edit it and then add these 2 blocks of code:
protected $appends = array('promotionImage');
here you just added the custom field. Now you tell the model how to fill it:
public function getPromotionImageAttribute() {
return (url('home/image/banner/'.$this->promotion_banner_id));
}
Now, you get your banners through your model:
static function m_get_promotion_banner(){
return \App\PromotionBanner::where('promotion_active','1')->get();
}
Now you can access your promotionImage propierty in your result
P.D:
In the case you are NOT using a model... Well, just create the file app\PromotionImage.php:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class PromotionImage extends Model
{
protected $appends = array('imageAttribute');
protected $table = 'promotion_banner';
public function getPromotionImageAttribute() {
return (url('home/image/banner/'.$this->promotion_banner_id));
}
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'promotion_banner_id','promotion_link','about_promotion','promotion_active'
];
just improving, in case you need to pass data inside the query
$url = 'home/image/banner/';
$query = $query->map(function ($object) use ($url) {
// Add the new property
$object->promotion_image = URL::to( $url . $object->promotion_banner_id);
// Return the new object
return $object;
});
I've been struggling with this all evening, and I'm still not sure what my problem is.
I've used ->get() to actually execute the query, and I've tried by ->toArray() and ->jsonserialize() on the data and it didn't fix the problem.
In the end, the work-around I found was this:
$task = Tasks::where("user_id", $userId)->first()->toArray();
$task = json_decode(json_encode($task), true);
$task["foo"] = "bar";
Using json_encode and then json_decode on it again freed it up from whatever was keeping me from editing it.
That's a hacky work-around at best, but if anyone else just needs to push past this problem and get on with their work, this might solve the problem for you.

Phalcon Volt Array is of string type

I have a Phalcon volt template I wanted to call in my custom helper, it will accept an array but the array sent to the helper is of string type.
In my list.volt I have this code,
{% set myfolder = data.foldername %}
{% set key = data.folderkey %}
{% set url = convert([myfolder, key]) %}
In my loader.php, I have declared the helper directory and have this code:
//$params should be single dimensional array
$compiler->addFunction('convert', function($params){
var_dump($params);
return MyCustomHelper::convert($params);
});
Will output string(31) "array($fname, $fkey)" instead of an array type. It made my helper stop working.
Anyone face this, I need it to be of an array type not string?
UPDATE: After applying #Nikolay Mihaylov suggestion.
Got an error
Fatal error: Class 'MyCustomUrlHelper' not found in cache/volt/%apps%%invo%%views%%test%%list.volt.php on line 56
In my services.php, I've included my helper directory
use Modules\Library\MyCustomUrlHelper;
/*
......
Some code here
..............................
....................
*/
$compiler->addFunction('convert', function($resolvedArgs, $exprArgs){
return 'MyCustomUrlHelper::convert('.$resolvedArgs.')';
});
In loader.php, i've registered the directory
........
.....................
$loader->registerDirs(array(APP_PATH.'Modules/Library'))->register();
...................
........................
In my Modules/Library directory, i have this MyCustomUrlHelper.php
<?php
namespace Modules\Library;
use Phalcon\Tag;
class MyCustomUrlHelper extends Tag
{
public function convert($params)
{
if(!is_array($params))
{
$params = array($params);
}
/*
..... some code here ...
.................
..........
*/
return $converted;
}
}
?>
Did i miss something else?
This is the correct way of extending volt:
$compiler->addFunction('convert', function($resolvedArgs, $exprArgs){
return 'MyCustomHelper::convert(' . $resolvedArgs . ')';
});
Will allow myself to quote docs:
Functions act as normal PHP functions, a valid string name is required
as function name. Functions can be added using two strategies,
returning a simple string or using an anonymous function. Always is
required that the chosen strategy returns a valid PHP string
expression.
More info in the following links:
Docs: https://docs.phalconphp.com/en/latest/reference/volt.html#id1
Similar question at SO: Sending variable from volt to custom function
Update: adding example code and output.
Volt custom function:
$compiler->addFunction('testArrays', function($resolvedArgs, $exprArgs) {
return 'Helpers\VoltCms::testArrays(' . $resolvedArgs . ')';
});
Helper file:
public static function testArrays($param)
{
d($param);
}
Usage and Output:
{{ testArrays(['asd', 'asd1']) }}
Array
(
[0] => asd
[1] => asd1
)

CakePHP 3 - Easiest way to retrieve a single field from the database

In CakePHP 2 I could do something like this:
$name = $this->User->field('name', ['email' => 'user#example.com']);
In CakePHP 3 you have to do something like this to achieve the same thing:
$users = TableRegistry::get('Users');
$query = $users->find()
->select('name')
->where(['email' => 'user#example.com']);
$name = $query->isEmpty() ? null : $query->first()->name;
Is there a simpler way to perform these kinds of operations? I'm not very familiar with the new ORM.
Edit: I have added an example of a class which adds this behavior for Cake 3:
https://stackoverflow.com/a/42136955/851885
It's possible to add this functionality to any Table via a custom behavior.
Save as src/Model/Behavior/EnhancedFinderBehavior.php
<?php
namespace App\Model\Behavior;
use Cake\ORM\Behavior;
/**
* EnhancedFinder behavior
*
* Behavior providing additional methods for retrieving data.
*/
class EnhancedFinderBehavior extends Behavior
{
/**
* Retrieve a single field value
*
* #param string $fieldName The name of the table field to retrieve.
* #param array $conditions An array of conditions for the find.
* #return mixed The value of the specified field from the first row of the result set.
*/
public function field($fieldName, array $conditions)
{
$field = $this->_table->getAlias() . '.' . $fieldName;
$query = $this->_table->find()->select($field)->where($conditions);
if ($query->isEmpty()) {
return null;
}
return $query->first()->{$fieldName};
}
}
Note: for CakePHP versions prior to 3.4, change the code to $this->_table->alias(), which was deprecated in favour of getAlias() in later versions.
Usage
Add the behavior to your class:
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class UsersTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('EnhancedFinder');
}
}
Now you can use the finder like Cake 2:
$name = $this->User->field('name', ['id' => 1]);
This might be simpler than yours
$users = TableRegistry::get('Users');
$name = $users->get(1)->name;
Make sure that when you use the get function, the parameter should be a primary key in the table.
No, there is not in CakePHP 3.x.
If you want that method back implement it either in a behavior or as a finder using a trait and use it with your table objects.

drupal_write_record doesn't take object

In drupal 6 i used to do something like this:
<?php
/*
* CLASS Example
*/
class example {
var $id = NULL;
var $title;
var $body;
.....
// Save
function save() {
$primary_key = ($this->id == NULL ? NULL : 'id');
if (drupal_write_record('mytabble', $this, $primary_key)) {
return TRUE;
} else {
return FALSE;
}
}
}
?>
This worked quite well. But in Drupal 7, the drupal_write_record only takes an array and no longer the object $this. The new db_merge also only takes an array.
Since i want to save the properties of my object to the database, the above code was very handy and generic for all kinds of classes.
Is there an alternative way to write an object to database, or a method to place objectproperties into a an array?
Any help will be appreciated!
Robert
drupal_write_record does take an object or an array. Guess your problem is caused somewhere else.
drupal_write_record($table, &$record, $primary_keys = array())
$record: An object or array representing the record to write, passed in by reference. If inserting a new record, values not provided in $record will be populated in $record and in the database with the default values from the schema, as well as a single serial (auto-increment) field (if present). If updating an existing record, only provided values are updated in the database, and $record is not modified.
More info on drupal_write_record for D7.

CakePHP, GET Parameters and routing

I am fairly new to cakephp but I have a question relating to urls and parameters. I would like to be able to have a url that looks like a standard url e.g:
http://www.mysite.com/controller/myaction?arg=value&arg2=val
I would like that url to map to an action in my controller as follows:
function myaction($arg = null, $arg2 = null)
{
// do work
}
I realize that cakephp has routing as described here, however, honestly this seems over engineered and results in a url string that is nonstandard.
In my current situation the url is being generated and invoked by an external (billing) system that knows nothing about cake and doesn't support the cake url format.
You can have your URL in any form. It's just CakePHP allows you to retrieve the variable passed through GET from the variable $this->params['url']
function myaction()
{
if(isset($this->params['url']['arg']))
$arg = $this->params['url']['arg'];
if(isset($this->params['url']['arg2']))
$arg2 = $this->params['url']['arg2'];
}
Solution in AppController for CakePHP 2.x
class AppController extends Controller {
....
/***
* Recupera los Named envias por URL
* si es null $key emtraga el array completo de named
*
* #param String $key
*
* #return mixed
*/
protected function getNamed($key=null){
// Is null..?
if(is_string($key)==true){
// get key in array
return Hash::get($this->request->param('named'), $key);
}else{
// all key in array
return $this->request->param('named');
}
}
...
}
I have a similar problem. Not because I have an external system, but because I don't like to put all parameters into the URL-path. In my example, I have some search queries that are assembled and passed to the controller. IMHO, these queries should be GET parameters and not part of the URL-path.
One advantage of using GET parameters is that the order of the given parameters is not important, in contrast to passing params via the URL path.
To solve this problem in a generic way, I'm replacing all method arguments with the value of the GET-param, if one with the same name is given:
class MyController extends AppController
{
function test($var1 = null, $var2 = "content2")
{
foreach (get_defined_vars() as $key => $value) {
if (isset($this->params['url'][$key])) {
$getvalue = $this->params['url'][$key];
$$key = $getvalue;
CakeLog::write("debug", "Setting:$key to $getvalue");
}
}
CakeLog::write("debug", print_r(get_defined_vars(), true));
}
}
Now I can access this controller method and pass parameters via GET like this:
http://myapp/mycontroller/test?var1=foo&var2=bar

Resources