CakePHP - how to modify FormHelper and add div after input? - cakephp

I am trying to modify FormHelper's behaviour to meet my application requirements. I would like to use native FormHelper but for all input I need to add some short message providing help to the user and describing the particular field.
My idea is to create my own helper and pass a help message as an argument. This function will modify form's inputDefaults setting and call a native FormHelper input function.
For example:
class MsgFormHelper extends AppHelper {
public function input($name, $message, $options) {
$this->_View->Form->_inputDefaults['after'] .= '<div>'.$message.'</div>';
return $this->_View->Form->input($name, $options);
}
}
But this solution notices this error:
Notice (8): Indirect modification of overloaded property
FormHelper::$_inputDefaults has no effect...
Is there some way how to modify "after" value in form's inputDefaults setting?

I probably found a solution (it works but I am not sure it doesn't violate some CakePHP's principles). Thanks for your opinions.
class MsgFormHelper extends AppHelper {
public function __construct(View $view, $settings = array()) {
parent::__construct($view, $settings);
}
public function input($name, $message, $options) {
$add_message = true;
if (isset($options['after'])) {
$options['after'] = trim($options['after']);
$add_message = empty($options['after']);
}
if ($add_message) {
$options['after'] = '<div class="input-help">' . $message . '</div>' . $this->_View->Form->_inputDefaults['after'];
}
return $this->_View->Form->input($name, $options);
}
}

You should be extending the FormHelper itself not AppHelper. Then in your controller use the aliasing feature so that you still use $this->Form->input() in your views but it would actually refer to your custom helper.
public $helpers = array('Form' => array('className' => 'MsgForm'))

Related

Error: Call to a member function newEntity() on boolean

I am new on cakephp. I have done all the required steps but still having trouble in saving data in database with cakephp
code of adduser function from Articlecontroller.php:
public function adduser()
{
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->getData());
// Hardcoding the user_id is temporary, and will be removed later
// when we build authentication out.
$user->user_id = 1;
if ($this->Users->save($user)) {
$this->Flash->success(__('Your article has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Unable to add your article.'));
}
$this->set('article', $user);
}
code of UserTable model:
<?php
// src/Model/Table/ArticlesTable.php
namespace App\Model\Table;
use Cake\ORM\Table;
class UsersTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
}
}
Data Base Table onto my locahost:
I think you forgot to load User model in controller. It should be fixed adding this line in function adduser() before first line. It should look like this.
public function adduser()
{
$this->loadModel('Users');
$user = $this->Users->newEntity();
...
Cakephp documentation.
https://book.cakephp.org/3.0/en/controllers.html#loading-additional-models
Well, you need to have a CakePHP application that is using the plugin. You need to add $this->loadComponent('Auth'); to your AppControllers initialize() method and configure it properly.
I highly recommend you to do the complete blog tutorial of the official CakePHP documentation or you won't have much fun with any plugin or anything else in the framework. It covers setting up Auth as well.
As other have started, when not in the controller related to the table you're accessing, you need to load the model and its related entities.
The current way of retrieving a table is through the table registry, as shown below:
use Cake\ORM\TableRegistry;
// Now $articles is an instance of our ArticlesTable class. This is how CakePHP 4 prefers it.
$articles = TableRegistry::getTableLocator()->get('Articles');
// Prior to 3.6.0
$articles = TableRegistry::get('Articles');
So for your public method, it should look like this:
public function adduser()
{
$usersTable = TableRegistry::getTableLocator()->get('Users');
$user = $usersTable->newEntity();
if ($this->request->is('post')) {
...code for handling post...
}
}
The CakePHP 3.x (and beyond) documentation regarding ORM outlines this well. While $this->loadModel('Articles') work, a quick search through the docs shows it is not frequently referred to neither in ORM section nor in examples.

CakePHP + nuSOAP - exposing a global function as a closure or using models outside of the controller?

I need to implement a SOAP service in a CakePHP controller. The exposed function names need to be in a global namespace (as in "Authenticate", not "SOAPController.Authenticate"). This forces me to implement Authenticate in the global namespace, since nuSOAP will deduce where to look for the function from its name. On the other hand, on the example below, I cannot use CakePHP's API, because I have no access to SOAPController's this. That is why I thought I should somehow declare some kind of a closure, that would resolve to 'Authenticate' in the global scope, while keeping a reference to this through bound parameter. Is that possible? Below is the erroneous code:
<?php
App::uses('AppController', 'Controller');
App::import('Vendor', 'nusoap',
array('file' => 'nusoap'.DS.'lib'.DS.'nusoap.php')
);
/**
* SOAP Controller
*
*/
class SOAPController extends AppController {
public function index()
{
$namespace = '';
$server = new soap_server();
$server->debug_flag = false;
$server->configureWSDL("PAI2WSDL", $namespace,
"http://localhost/pai2/SOAP/?wsdl");
$server->wsdl->schemaTargetNamespace = $namespace;
function Authenticate($login,$haslo){
$this->log("TEST"); //won't work!
return true;
}
$server->register('Authenticate',
array('ContractorId' => 'xsd:int', 'Password'=>'xsd:string'),
array('return' => 'xsd:boolean'),
$namespace,
$namespace . '#Authenticate',
'rpc',
'encoded'
);
$HTTP_RAW_POST_DATA = isset($GLOBALS['HTTP_RAW_POST_DATA'])
? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
$server->service($HTTP_RAW_POST_DATA);
$this->autoRender = false;
exit();
}
}

Cakephp validation bug in controller?

I am trying to invalidate a field by a condition in controller instead of Model.
$this->Model->invalidate('check_out_reason', __('Please specify check out reason.', true));
The above won't work to invalidate the field. Instead, I need the below:
$this->Model->invalidate('Model.check_out_reason', __('Please specify check out reason.', true));
However, if I wish get the error message show up in the "field" itself ($this->model->validationErrors), it needs to be "check_out_reason" instead of "Model.check_out_reason". That means, I can't get the error message to show up in the field itself if I wish to invalidate the input in controller.
May I know is this a bug in CakePHP?
i created a test controller called "Invoices", just for testing, and i developed the following function
public function index(){
if (!empty($this->request->data)) {
$this->Invoice->invalidate('nombre', __('Please specify check out reason.'));
if ($this->Invoice->validates()) {
// it validated logic
if($this->Invoice->save($this->request->data)){
# everthing ok
} else {
# not saved
}
} else {
// didn't validate logic
$errors = $this->Invoice->validationErrors;
}
}
}
i think it worked for me
Change the field "nombre" for your field called "check_out_reason" to adapt the function to your code
I found a workaround for manual invalidates from controller. Reading a lot on this issue I found out that the save() function doesn't take in consideration the invalidations set through invalidate() function called in controller, but (this is very important) if it is called directly from the model function beforeValidate() it's working perfectly.
So I recommend to go in AppModel.php file and create next public methods:
public $invalidatesFromController = array();
public function beforeValidate($options = array()) {
foreach($this->invalidatesFromController as $item){
$this->invalidate($item['fieldName'], $item['errorMessage'], true);
}
return parent::beforeValidate($options);
}
public function invalidateField($fieldName, $errorMessage){
$this->invalidatesFromController[] = array(
'fieldName' => $fieldName,
'errorMessage' => $errorMessage
);
}
After that, make sure that your model's beforeValidate() function calls the parent's one:
public function beforeValidate($options = array()) {
return parent::beforeValidate($options);
}
In your controller for invalidating a field use next line:
$this->MyModel->invalidateField('fieldName', "error message");
Hope it helps! For me it's working!

Code Igniter "created" and "modified"

I'm new to CI but I know CakePHP pretty well. I've searched the documentation of CI and haven't found this.
In CakePHP the fields "created" and "modified" were automatically populated by the "save" function. Does CI have two fields like that? Or can he automatically populate something?
EDIT:
Ok.. I've extended the CI_Model class and now, is there a function such as beforeSave (from cake)? And shouldn't I rather extend the DB class (I use db->set and db->insert);
Is this ok?
<?php
class spj_Model extends CI_Model {
var $table;
function __construct() {
parent::__construct();
}
function insert($data) {
$this->load->helper('date');
$data['created'] = date('Y-m-d H:i:s',now());
$data['modified'] = date('Y-m-d H:i:s',now());
return $this->db->insert($this->table,$data);
}
function update($data,$where=array()) {
$this->load->helper('date');
$data['modified'] = date('Y-m-d H:i:s',now());
return $this->db->ubdate($this->table,$data, $where);
}
}

How can I set the title_for_layout in the default PagesController?

I cannot set title_for_layout in the PagesController that comes by default with CakePHP 1.3.
I am using the following code in the display function:
$this->set('title_for_layout','some title');
What am I doing wrong?
In your controller, the corresponding value is $this->pageTitle.
UPDATE
Oops, as noted in the comments, this is the 1.2 solution. 1.3 possibilities (after doing some research) include:
Ensuring that $title_for_layout is being echoed in the layout
Placing the $this->set() code in the view rather than in the controller
If you'd like to mimic the behavior of cake 1.2, you can do the following:
In your app_controller, create the following method:
in app/app_controller.php (you may need to create this file if you haven't already)
public $pageTitle;
public function beforeRender() {
$this->set('title_for_layout', $this->pageTitle);
}
Then in any of your action methods, you may then use the pageTitle as you would in 1.2.
public function index() {
$this->pageTitle = 'Some Title';
}
The beforeRender() method will be called after your controllers have finished processing, but prior to the layout being rendered, thus allowing you to set variables for the layout.
In the action method, try this:
function index()
{
$this->pageTitle= 'Your Title';
}
Just thought I'd add to any new people finding this solution, you can do $this->set('title', $title); in CakePHP 1.3 inside the controller and the title will be rendered automatically.
You can use :
$this->assign('title', 'Some title');
and in ctp :
<title><?= $this->fetch('title'); ?></title>
It work in CakePHP 3.0
For CakePHP 3.x,
you can follow the advice here
Essentially,
Inside UsersController.php:
$this->set('title', 'Login');
Inside src/Template/Layouts/default.ctp
above the $this->fetch('title');
write:
if (isset($title)) {
$this->assign('title', $title);
}
Use beforeRender() instead. Put the following in AppController:
class AppController extends Controller {
var $content;
function beforeRender() {
$this->set('content',$this->content);
}
}
And in the controller, you can just do:
function index() {
$this->content['title'] = '123'; }
That'll do the trick.

Resources