Error: Cannot be accessed directly CakePHP - cakephp

Element file I am calling:
$brand = $this->requestAction('brands/buyer_getnames/');
Action file I am calling:
public function buyer_getnames(){
$newid=$this->Auth->User('brand_id');
$name=$this->Brand->find('first',array('conditions'=>array('Brand.id'=>$newid),'recursive'=>-1));
return $name['Brand']['name'];
}
Getting error below..!!
Private Method in BrandsController
Error: BrandsController::buyer_getnames() cannot be accessed directly.
Please help

Request action respects normal url routing rules
If you're using prefix routing then you can't access function prefix_foo() via a url of the form /controller/prefix_foo - it needs to be the corresponding prefix url: /prefix/controller/foo.
As such your request action call should be:
$brand = $this->requestAction('/prefix/brands/getnames/');
Note that if the only thing that method does is call a model method, you're better off simply doing:
$model = ClassRegistry::init('Brand');
$brand = $model->someMethod();

You can allow unauthorized access to action if your action is requested with requestAction method.
For example:
public function beforeFilter() {
parent::beforeFilter();
if ($this->request->is('requested') && $this->request->params['action'] == 'index') {
$this->Auth->allow(array('index'));
}
}
This may also work (haven't tested):
public function index() {
if ($this->request->is('requested')) {
$this->Auth->allow(array('index'));
}
}
let me know if i can help you more.

Related

Auth.afterIdentify is not firing

I need to change something in the user session after it was started. This is temporary, so using an event such as Auth.afterIdentify is what I'm looking for.
What I tried
I have largely referred to this answer on how to even approach this.
I have tried using an anonymous function callback as well as a controller callback method
Following the Auth.afterIdentify documentation I have added implementedEvents
Made sure implementedEvents has a + parent::implementedEvents(), otherwise the controller kept showing me the home page
What I have
Here's my current src/Controller/AppController.php:
<?php
namespace App\Controller;
use Cake\Controller\Controller;
use Cake\Event\Event;
class AppController extends Controller implements \Cake\Event\EventListenerInterface
{
public function initialize()
{
parent::initialize();
// …
$this->loadComponent('Authentication.Authentication');
// Trying with an anonymous function
\Cake\Event\EventManager::instance()->on('Auth.afterIdentify', function ($event) {
Log::write( // noticed when posting this question, should have thrown an error
'info',
'Testing: ' . $event->getSubject()->id
);
debug($event);exit;
});
// Trying a controller callback
\Cake\Event\EventManager::instance()->on('Auth.afterIdentify', [$this, 'afterIdentify']);
}
public function beforeFilter(\Cake\Event\Event $event)
{
parent::beforeFilter($event);
$this->set('myAuth', $this->Authentication->getResult());
$this->set('myUser', $this->Authentication->getIdentity());
}
public function afterIdentify(CakeEvent $cakeEvent, $data, $auth) {
debug([
'$cakeEvent' => $cakeEvent,
'$data' => $data,
'$auth' => $auth,
]);exit;
}
public function implementedEvents()
{
return [
'Auth.afterIdentify' => 'afterIdentify',
] + parent::implementedEvents();
}
}
What doesn't work
It seems neither of the above event listeners is being called. No CakePHP logs are being updated (not even with errors), although they normally work.
What I expected to happen
Calling Log::write without declaring where it comes from should have thrown (and logged) an error
The debug() information was not displayed
Removing the public function afterIdentify method should have caused an error; it didn't – meaning the controller isn't even looking for it
You are mixing up the old auth component and the new authentication plugin, the Auth.afterIdentify event belongs to the former.
The authentication plugin's authentication component has a Authentication.afterIdentify event, but this only applies to authenticators that are stateful and do not implement automatic persisting. So out of the box this only applies to the Form authenticator, and the event is being triggered once on the request where the user was authenticated via the form, on subsequent requests where they are authenticated via for example the Session authenticator, the event is not being triggered.
public function initialize()
{
parent::initialize();
// ...
$this->loadComponent('Authentication.Authentication');
$this->Authentication->getEventManager()->on(
'Authentication.afterIdentify',
function (
\Cake\Event\EventInterface $event,
\Authentication\Authenticator\AuthenticatorInterface $provider,
\Authentication\IdentityInterface $identity,
\Authentication\AuthenticationServiceInterface $service
) {
// ...
$identity['foo'] = 'bar';
$this->Authentication->setIdentity($identity);
}
);
}

How to get this code work to store in Laravel. No errors and no storage

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 .

Laravel: resetting password without getting redirect response

I am building an angular application and want to implement password reset. However, default laravel config doesn't appear to allow one to do this using purely XMLHttpRequest ($http.post) requests and responds with a 302 redirect.
I managed to get postLogin and postRegister to work without issuing redirects by implementing said methods in authController class and returning a json response, doing this overrides the default laravel implementation of said methods. No such luck with postEmail and it appears the method is not hit at all, I just get a 302 response back immediately.
Ideally, other than to check their E-mail, I don't want the user to leave the single page angular application at all.
So 1. User posts E-mail to postEmail -> Email with reset link or better 'reset code' is sent to E-mail address -> User then inputs the reset token code into the already open web app or if it can't be done, browse to reset password page opened in new tab.
I tried implementing postEmail method as such:
public function postEmail(Request $request)
{
$this->validate($request, ['email' => 'required|email']);
$response = Password::sendResetLink($request->only('email'), function (Message $message) {
$message->subject($this->getEmailSubject());
});
switch ($response) {
case Password::RESET_LINK_SENT:
return response()->json(['msg' => 'A reset link has been sent to your E-mail'], 200);
case Password::INVALID_USER:
return response()->json(['msg' => 'This E-mail cannot be found in our system'], 200);
}
}
Also, where is template for the E-mail with the reset link that laravel sends out ?
You can create a PasswordController within the App\Http\Controllers\Auth namespace to extend the password reset methods.
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\PasswordBroker;
use Illuminate\Foundation\Auth\ResetsPasswords;
class PasswordController extends Controller
{
use ResetsPasswords;
public function postEmail(Request $request)
{
}
}
To overwrite the email templates you can create a reminder.blade.php in the app/views/emails/auth directory, or change the location of the template file in the app/config/auth.php config.
while the accepted answer is completely valid, another solution without overriding the original notification class is as follows, ResetPassword provides a static method called createUrlUsing which accepts a Closure, So we can override the URL as something like the below:
use Illuminate\Support\Facades\Password;
use Illuminate\Auth\Notifications\ResetPassword;
...
$status = Password::sendResetLink(
['email' => $args['email']],
function ($user, $token) {
ResetPassword::createUrlUsing(function ($notifiable, $token) {
// This is where you override the URL, you can also take a look at
// the `url`, `action` and `route` functions in Laravel and skip
// `sprintf` if you prefer to stick to Laravel functions only.
return sprintf(
"%s/%s/?token=%s&email=%s",
config('your.optional.frontend_url'),
config('your.optional.password_reset'),
$token,
$notifiable->getEmailForPasswordReset(),
); // frontend_url/password_url/?token=TOKEN&email=EMAIL
});
return $user->notify(new ResetPassword($token));
}
);
// This is an optional way to handle the final response, you can convert it to
// JSON or ignore it.
return $status === Password::RESET_LINK_SENT
? ['status' => __($status)]
: throw new Error(__($status));
This piece of code should be placed at a new route to handle password reset requests instead of using the default Laravel one.

cakephp mapResource to action

I have setup cakephp to accept json but when I add the
Router::mapResources('members');
it breaks my search plugin. Can I just target a particular action in the controller members instead of all the actions.
cakephp 2.1
Controller: members
action: api
Seems like that's not possible, but you can use it like this (in MembersController.php):
public function beforeFilter() {
$action = reset(explode(".", $this->request->params['action']));
if($action == 'api') {
$this->viewClass = 'Json'; // or whatever you want
$this->setAction($action);
}
}

AppModel aftersave, get controller function name

I'm trying to log every write operation so I'm using the afterSave and afterDelete callbacks in AppModel. Basically I need to log(for the moment): the model , the controller function, the loggedin user data and the remote ip
It seems that I was able to get all of them but I don't know how to get the controller function name.
This is the aftersave function I have now:
public function afterSave($created) {
App::uses('Folder', 'Utility');
$month = date("y-m");
if(!is_dir('../tmp/logs/'.$month)) {
$dir = new Folder('../tmp/logs/'.$month, true);
}
App::uses('CakeSession', 'Model/Datasource');
$user_id = CakeSession::read('Auth.User.username');
if($created) {
$id = 'New';
} else {
$id = $this->data[$this->alias]['id'];
}
$str = 'WRITE Action. Model: '.$this->alias.'. Controller: functon_name. ID:'.$id.'. Username: '.$user_id.'. Client IP: '.$this->getIP();
CakeLog::write($month.'/'.date("d-m-y"), $str);
}
Thanks
You're doing this on the model, which has no knowledge of the controller (and really shouldn't). I'd suggest copying the CakeRequest object to the model so you have that information. Here's one way:
//controller
function beforeFilter() {
$this->{$this->modelClass}->request = $this->request;
}
Then you can access the request object from the model. Use $this->request['params']['action'] to get the current dispatched action.
It's worth suggesting that you might want to move this to the read() method on a custom datasource, as afterSave() can possibly be skipped and therefore not logged.

Resources