in CakePhp 2.x I was able to extend a core helper with my own class and use it in view files.
In CakePhp 3.8 I want to extends FormHelper, for example to change some default properties.
I've tried this:
class MyFormHelper extends FormHelper
{
public function control($fieldName, array $options = [])
{
$options += [
'label' => false,
'class' => 'form-control'
];
return parent::control($fieldName, $options);
}
}
and my options are correctly taken but many other original behavior does not works anymore.
For example CakePhp doesn't recognize the fields type (boolean type fields are shown as text inputs and not checkboxes) and doesn't insert the value of field inside a edit form.
Do you know why?
I've also tried calling parent method in this way without success:
[...]
// return parent::control($fieldName, $options);
return $this->__xformCallParent(array($this, 'parent::control'), func_get_args());
[...]
private function __xformCallParent($call, $args)
{
if (PHP_VERSION >= 5.3 && is_array($call)) {
$call = $call[1];
}
return call_user_func_array($call, $args);
}
I've found the answer!
Just insert your custom FormHelper inside src/View/Helper/FormHelper.php file and declare it with this code:
namespace App\View\Helper;
use Cake\View\Helper\FormHelper as Helper;
class FormHelper extends Helper
{
public function control($fieldName, array $options = [])
{
$options += [
'label' => false,
'class' => 'form-control'
];
[...]
return parent::control($fieldName, $options);
}
}
replacing your code inside the method.
I've just removed the label by default and add a custom class to input elements.
Related
Hay. Currently I'm creating a function in Behaviour in CakePHP 2.10.12 as shown below :
<?php
App::uses('CakeTime', 'Utility');
App::uses('CakeNumber', 'Utility');
class ConverterBehavior extends ModelBehavior {
private $timezone;
private $locale;
private $currency;
function hellow() {
return "Hellow from behavior";
}
function initConverter($locale, $timezone, $currency) {
$this->locale = $locale;
$this->timezone = $timezone;
$this->currency = $currency;
setlocale(LC_ALL, $locale);
}
public function getCurrentLocale() {
return $this->locale;
}
function convertCurrency($currencyAmount) {
return CakeNumber::currency($currencyAmount, $this->currency);
}
function convertDate($date) {
return CakeTime::i18nFormat($date, null, false, $this->timezone);
}
}
?>
Then above behavior is used by my model as shown below :
<?php
class Test extends AppModel {
public $actsAs = array('Converter');
}
And then I call the function that I created from behavior in my Controller as shown below :
public function converterModel() {
$this->Test->initConverter('ja_JP', 'Asia/Tokyo', 'JPY');
$temp = $this->Test->convertCurrency(23456789901.123456);
debug($this->Test->hellow());
// $this->set('jpDate', $this->Test->convertDate(new DateTime));
}
The problem is initConverter cannot be initialized. I check the variabel that are inputed from controller and all of those variabel are null (it's weird). But when I call the hellow() (function in behavior), the result is displayed in my view. So is there something missing here ?
Thank you
Note :
This is the error message displayed in my view :
Look at the warning/notice, you are receiving an object where you expect a string/number.
The first argument of an externally invoked behavior method will always be the instance of the model that the behavior is attached to, ie your method signatures should be like:
function initConverter(Model $model, $locale, $timezone, $currency)
function convertCurrency(Model $model, $currencyAmount)
// etc...
See also
Cookbook > Models > Behaviors > Creating Behaviors
Cookbook > Models > Behaviors > Creating behavior methods
I want to create widget for Cakephp formHelper.
I created a widget file named ImageWidget.php in View/Widget directory (I don't know if this is the right directory but phpstorm loads it so it seems fine)
<?php
namespace App\View\Widget;
use Cake\View\Form\ContextInterface;
use Cake\View\Widget\WidgetInterface;
class ImageWidget implements WidgetInterface
{
protected $_templates;
public function __construct($templates)
{
$this->_templates = $templates;
}
public function render(array $data, ContextInterface $context)
{
$data += [
'name' => '',
];
return $this->_templates->format('myTestTemp', [
'name' => $data['name'],
'attrs' => $this->_templates->formatAttributes($data, ['name'])
]);
}
public function secureFields(array $data)
{
return [$data['name']];
}
}
?>
But I got this error:
Cannot find template named 'myTestTemp'.
I don't know where should I put my template or what is this template at all
(is it like cakephp normal template?)
my template file is like this:
//in myTestTemp.ctp file
<p>some html for test</p>
I tried to put file in these directories and it doesn't work
src/Template
src/View
src/View/Widget
thanks
Like any other widget, your widget works with string templates, and it relies on the template being set in the form helper (respectively to be set on the \Cake\View\StringTemplate object that is being passed to the widgets constructor), like for example:
$this->Form->setTemplates([
'myTestTemp' => '<p {{attrs}}>some html for test</p>'
]);
See also
Cookbook > Views > Helpers > Form > Building a Widget Class
Cookbook > Views > Helpers > Form > Customizing the Templates FormHelper Uses
Im currently making a Yii2 RESTful system with AngularJs.
In my database i've got several columns that i want to be able to return when doing a particular call from a certain point in my system.
The problem i'm having is how do i return only a handful of fields eg(id, title and stub) from the restful call in another part of my system so that it ignores other fields in the table.
I would ideally like it to work in a similar way to how a Models rules work with scenarios in yii.
There are two methods, I think:
1. use params
// returns all fields as declared in fields()
http://localhost/users
// only returns field id and email, provided they are declared in fields()
http://localhost/users?fields=id,email
// returns all fields in fields() and field profile if it is in extraFields()
http://localhost/users?expand=profile
// only returns field id, email and profile, provided they are in fields() and extraFields()
http://localhost/users?fields=id,email&expand=profile
2. overriding model's fields()
// explicitly list every field, best used when you want to make sure the changes
// in your DB table or model attributes do not cause your field changes (to keep API backward compatibility).
public function fields()
{
return [
// field name is the same as the attribute name
'id',
// field name is "email", the corresponding attribute name is "email_address"
'email' => 'email_address',
// field name is "name", its value is defined by a PHP callback
'name' => function () {
return $this->first_name . ' ' . $this->last_name;
},
];
}
// filter out some fields, best used when you want to inherit the parent implementation
// and blacklist some sensitive fields.
public function fields()
{
$fields = parent::fields();
// remove fields that contain sensitive information
unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);
return $fields;
}
more detail, refer to https://github.com/yiisoft/yii2/blob/master/docs/guide/rest-resources.md
You may use scenarios method inside your model for this, but you will have to extend a bit toArray method in order to make it work properly:
public function scenarios()
{
return array_merge(parent::scenarios(), [
'simple_info' => [
'email',
'name',
],
'login' => [
'id',
'email',
'name',
'auth_token',
],
]);
}
public function toArray(array $fields = array(), array $expand = array(), $recursive = true)
{
$scenarios = $this->scenarios();
$scenario = $this->getScenario();
if (!empty($scenarios[$scenario])) {
$data = parent::toArray($fields, $expand, $recursive);
return array_intersect_key($data, array_flip($scenarios[$scenario]));
}
return parent::toArray($fields, $expand, $recursive);
}
After this you may simply do something like this:
$model = new LoginForm();
if ($model->load(Yii::$app->request->post(), '') && $model->login()) {
$user = $model->getUser();
// Lets change scenario to login in order to get `auth_token` for authorization
$user->setScenario('login');
$user->generateAuthKey();
$user->save(FALSE);
return $user;
} else {
return $model;
}
As a side note (expanding on the answer from #Ganiks), if you are manually returning the list of Models, you will need to return them as a DataProvider (rather than simply as an array of Models) for the fields parameter to have an effect.
For example, if you do something like this...
class UserController extends yii\rest\Controller
{
public function actionIndex()
{
return User::find()->all(); // Not what you want
}
// ...
}
... then the fields parameter will not have the desired effect. However, if you instead do this...
class UserController extends yii\rest\Controller
{
public function actionIndex()
{
return new ActiveDataProvider([
'query' => User::find(),
'pagination' => false,
]);
}
// ...
}
... then the returned fields will only be those you specified in the fields parameter.
This might be a naive question since I am new to cakephp.
I am working on a project where there are many layouts, helpers and elements. Because this is a mutli-language site, I am intercepting the final rendered output to do some language conversion(so each visitor only sees his native language on the site including user input).
I've managed to convert most of the layouts and helpers by adding code in two places: AppController's afterFilter() and AppHeler's afterRender() function. But I can't figure out a centralized way to handle the elements and there are dozens of them.
So here are my questions: do all elements in cakephp have a common ancestor class? If so, does this ancestor class have callback functions like afterRender()?
Many thanks!
I'm not sure such a callback exists specific for 'elements', but looking at the source code, View::element() renders an element using the same _render() method as the View itself, and should trigger beforeRender() and afterRender()
Creating a custom View Class and Custom Callbacks
You may use a custom 'View' class and override the element() method, for example to have your own 'custom' callbacks being triggered in helpers
Something like this;
app/view/app_view.php
class AppViewView extends View {
/**
* custom 'element()' method, triggers custom
* before/aferRenderElement callbacks on all loaded helpers
*/
public function element($name, $params = array(), $loadHelpers = false)
{
$this->_triggerHelpers('beforeRenderElement');
$output = parent::element($name, $params, $loadHelpers);
$this->_triggerHelpers('afterRenderElement');
}
/**
* Names of custom callbacks
*/
protected $_customCallBacks = array(
'beforeRenderElement',
'afterRenderElement',
);
function _triggerHelpers($callback)
{
if (!in_array($callback, $this->_customCallbacks)) {
// it's a standard callback, let the parent class handle it
return parent::_triggerHelpers($callback);
}
if (empty($this->loaded)) {
return false;
}
$helpers = array_keys($this->loaded);
foreach ($helpers as $helperName) {
$helper =& $this->loaded[$helperName];
if (is_object($helper)) {
if (
is_subclass_of($helper, 'Helper')
&& method_exists($helper, $callback)
) {
$helper->{$callback}();
}
}
}
}
}
Then, in your AppController specify the 'view' class to use;
class AppController extends Controller {
public $view = 'AppView';
}
I have one team array and want that team name every where to show team name.It is possible to built a global function which can return team name and I call that function from my view means ctp file.
please try this for west:
<?php
// controller name like app,users
// action name like getdata should be in controller
// and you can send parameter also
$output = $this->requestAction('controllerName/actionName/'.$parameter);
?>
There are multiple approaches to this. What I cannot tell from your description is exactly what you are looking for. If it is simply to create an array of items that is accessible in your views, I would put it in app_controller.php
var $teams = array('team1', 'team2', 'team3');
beforeFilter() {
$this->set('teams', $this->teams);
}
Then in your view, you can access the array by the variable: $teams
If you only want to call teams on certain views, it may not be a good idea to set this variable for EVERYTHING. You can get around it by setting up a function in app controller.
function get_teams_array() {
$teams = array('team1', 'team2', 'team3');
return $teams;
}
Then put together an element that will call this function:
views/elements/team.ctp
<?php
$teams = $this->requestAction(
array('controller' => 'app', 'action' => 'teams'),
array('return')
);
/** process team array here as if it were in the view **/
?>
Then you can just call the element from your view:
<?php echo $this->element('team'); ?>
You can add in your /app/config/bootstrap.php file something like:
Configure::write('teams', array('team1', 'team2'));
Then everywhere you can get that array with:
$teams = Configure::read('teams');
and use it.
In CakePHP 3.*, you can use Helpers.
https://book.cakephp.org/3.0/en/views/helpers.html#creating-helpers
1 - Create your helper inside src/View/Helper:
/* src/View/Helper/TeamHelper.php */
namespace App\View\Helper;
use Cake\View\Helper;
class TeamHelper extends Helper
{
public function getName($id)
{
// Logic to return the name of them based on $id
}
}
2 - Once you’ve created your helper, you can load it in your views.
Add the call $this->loadHelper('Team'); inside /src/View/AppView.php:
/* src/View/AppView.php */
class AppView extends View
{
public function initialize()
{
parent::initialize();
$this->loadHelper('Team');
}
}
3 - Once your helper has been loaded, you can use it in your views:
<?= $this->Team->getName($id) ?>