I am new to CakePHP.
I would like to use the model validate mechanism, but I'm having trouble overriding the errors that are displayed. I am building an API where all the views need to be rendered in JSON and I have a JSON format that all errors need to output as. I've defined a custom AppError class and I have successfully be able to define custom errors in this format there.
Is there a way to use the AppError class to override the output of the error messages coming from validation?
Thanks.
I came up with a solution by adding these methods to my AppModel class:
function validates($options = array()) {
$result = parent::validates($options);
if (!$result) {
$this->_validateErrors();
}
return $result;
}
function _validateErrors() {
foreach ($this->validationErrors as $code) {
$this->cakeError('apiError', array('code' => $code)); // Custom JSON error.
return;
}
}
I then manually call $this->Model->validates() before a Model::save() call in my controller. This seems to be working well.
As far as I know, there's no direct way to get validation errors from within your AppError class. The way around it would be to create an AppModel class in app/app_model.php and use the onError() callback method to pass the error to your AppError class.
// app/app_model.php
class AppModel extends Model {
public function onError() {
// Pass the errors to your AppError class
AppError::someErrorMethod($this->getErrors());
}
}
Related
How do i access another model within a model in cakephp4.2? The docs on this issue isnt clear to me and i can then run a query on this ? TableRegistry is deprecated now.
error Unknown method "getTableLocator" called on App\Model\Table\LessonsTable
//none of these no longer work
in model {
use Cake\ORM\Locator\LocatorAwareTrait;
class LessonsTable extends Table
{
..
private function getlessonRevenue(){
//$clients = $this->getTableLocator()->get('Clients');
// $cleints = TableRegistry::get('Clients');
// $this->Table = TableRegistry::get('Clients');
$clients = $this->getTableLocator()->get('Clients');
https://api.cakephp.org/4.0/class-Cake.ORM.TableRegistry.html
Try:
<?php
use Cake\ORM\Locator\LocatorAwareTrait; //<------------ add here
class ArchivesTable extends Table
{
use LocatorAwareTrait; // <--------------------------- and add here
public function myMethod()
{
$clients = $this->getTableLocator()->get('Clients');
}
and read https://book.cakephp.org/4/en/orm/table-objects.html#using-the-tablelocator
and learn how to use php trait https://www.phptutorial.net/php-tutorial/php-traits/
I can't store name and IP address to DB. I created a table 'info' with appropriate fields by running php artisan migrate.
A schema
Schema::create('info', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('ip');
$table->timestamp('created_at')->nullable();
});
A model for Info
class Info extends Model
{
protected $fillable = ['ip', 'name'];
}
Maybe the problem is in my HomeController where I get those variables?
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Info;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Request;
class HomeController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function store(Request $request) {
Info::create(['info' => $request->input('info')]);
}
public function index()
{
if (Auth::check())
{
$name = Auth::user()->name;
$ip = Request::ip();
\App\Events\eventIp::dispatch($ip,$name);
return view('home');
}
}
}
My routes in web.php
Route::post('/home','HomeController#store');
Route::get('/home', 'HomeController#index')->name('home');
});
But it doesn't work. Gives no errors and no records in DB.
Something make me think that it have to do with my index function. I got info in function index and maybe function store doesn't have a clue what I mean.
A controller action is basically a method that usually gets executed when you open an url (as you connect them to routes).
In your example you have connected two routes to their respective actions:
Route::post('/home','HomeController#store');
Route::get('/home', 'HomeController#index')->name('home');
Now, when you log in succesfully, imagine that you end up in the page with url http://localhost:8000/home in your web browser.
The key difference is the method which you use to call your route (you can get an overview of the differences here), in your case you are using GET method.
The resulting action executed it the one associated to /home route with the GET method, that is the HomeController#index action (or method).
The store method, although is in the same HomeController class, doesn't get triggered unless you execute the /home route, but with the POST method.
You can confirm that if you put a debug message in each of the methods like this:
class HomeController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function store(Request $request)
{
echo 'I will not be executed';
}
public function index()
{
echo 'I have been executed';
}
}
If you want to simply save a info record when you visit the /home route with the GET method, you can put the save in the index method itself and get rid of the store method:
class HomeController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
// You can omit Auth::check() because you are using the auth middleware
// that exactly does this job.
Info::create([
'name' => Auth::user()->name,
'ip' => Request::ip(),
]);
return view('home');
}
}
Keep in mind that doing in this way you will get a new database record for each page view you make to that route (if you keep refreshing the page, you should see new records being added to database).
Update
When you use Eloquent Models, laravel will look for a table named after the pluralized model name (Info model will try to use infos table).
However you created a table named info. To solve that you can either rename the table and rerun the migration with php artisan migrate:refresh (it will delete all the existing data in the database you are using for your laravel app)
Or specify the table name to use for that laravel model:
class Info extends Model
{
protected $table = 'info';
protected $fillable = ['ip', 'name'];
}
How are you calling the functions? There is a couple of things wrong with your code, but you're saying there are no errors at all.
Firstly, your Info::create call does not need the ['info' => $request->input('info')] info. This is because your Info model has no database property called info, but normally you would get an obvious error with the approach, which is why I expect you are also calling the store method incorrectly.
Call the create method like so:
$infoModel = Info::create(['name' => $request->input('name'), 'ip' => $request->input['ip']]);
or, if you can guarantee your $request only contains the needed fields (properly validated), you can just do
$infoModel = Info::create($request->all());
Add a little more info to the question on how you are calling store and we can probably solve the rest of your problem.
Within your store function inside HomeController , use
Info::create([
'name' => Auth::user()->name,
'ip' => Request::ip(),
]);
and make sure Info model is imported.
Also make sure your route has the call to store function while POSTing data .
How I use App controller methods or properties in my own helper method in cake php 2.X?
I have method called get_lan() in App controller and this is my helper but when i run get error message:
Error: Call to undefined function get_lan()
File: C:\wamp\www\app\View\Helper\current_langHelper.php
Line: 8"
Code:
<?php
App::uses('AppHelper', 'View/Helper');
class current_langHelper extends AppHelper {
public $helpers = array('Html');
public function current_lang()
{
var_dump(get_lan());
}
}
Helpers do not call Controller methods. In other words, the Helper does not "see" the Controller members. The Controller should pass information to the View/Helper via the set() method. You could have your get_lang() method (probably better to name it setLang()) to pass the language to the view, something like:
private setLang(){ $this->set('lang',$selectedLanguage); }
You could then use this variable in your view using:
var_dump($lang);
I used AkismetComponent in AkisHelper and my AkisHelper code is:
<?php
App::uses('AppHelper', 'View/Helper');
class AkisHelper extends AppHelper {
public $helpers = array('Html');
public $components = array('Akismet');
function isValid() {
if ($this->Akismet->isKeyValid()) {
echo 'OK';
} else {
echo 'Error';
}
}
}
But this error occured:
Error: Call to a member function isKeyValid() on a non-object
File: /var/www/cakeblog/app/View/Helper/AkisHelper.php
Line: 10
Please help me to solve my problem.
Thanks
Your not supposed to be able to n_n.. It's not MVC, it's like trying to call a controller method inside the view.
However you could always pass a variable to the view in your Akismet component, something like:
class AkismetComponent extends Component {
private $controller;
public function initialize($controller) {
$this->controller = $controller;
//here I pass a variable to the view
$this->controller->set('isKeyValid',$this->isKeyValid());
}
and in your view use it like any other variable:
if(isset($isKeyValid) && $isKeyValid){
}
Anyway, if you don't want to change the component, you could still pass the variable from the controller.
The view should be used only to display the information. Helpers should be only functions to help you with that, but they are not supposed to do business logic.
Hope this helps
I need to know in a helper in a CakePHP application if the device is mobile, I would love to use $this->RequestHandler->isMobile(), but the request handler component is not available in helpers. Any ideas?
Thanks!
You can import the class and use it anywhere in the framework like so:
App::import('Component', 'RequestHandler'); // import class
$requestHandler = new RequestHandlerComponent(); // instantiate class
$isMobile = $requestHandler->isMobile(); // call method
var_dump($isMobile); // output: bool(true) or bool(false)
(Tested from helper and gives correct results for Firefox and iPhone)
Also, any options you set in the Controller::helpers property will be passed to the helper:
class AppController extends Controller {
public $components = array(/*...*/, 'RequestHandler');
public $helpers = array(/*...*/, 'MyHelper');
public function beforeFilter() {
$this->helpers['MyHelper']['mobile'] = $this->RequestHandler->isMobile();
}
}
You can catch the options array in your helper's constructor:
class MyHelper extends AppHelper {
protected $_defaultOptions = array('mobile' => false);
public function __construct($options) {
$this->options = array_merge($this->_defaultOptions, $options);
}
}
The accepted answer suggests using a component inside a helper which should be avoided as components are for use solely in controllers and will result in errors as mentioned by Anupal.
The simple solution is to use the CakeRequest class that RequestHandlerComponent uses. So in your helper you can do:-
App::uses('CakeRequest', 'Utility');
$isMobile = (new CakeRequest())->is('mobile');