I'm using CakePhp 2.2 and I have this simple controller, named ProvidersController:
<?php
class ProvidersController extends AppController {
public $components = array('Session', 'Social');
public $layout = false;
public function facebook(){
$this->Social->auth('facebook', 'success', 'error');
$this->render(false);
}
public function google(){
$this->Social->auth('google', 'success', 'error');
$this->render(false);
}
private function success(){
}
private function error(){
}
}
?>
and this Component, named SocialComponent:
<?php
class SocialComponent extends Component {
public function auth($provider, $success, $error){
}
}
?>
as you can see I have created success() and error() methods inside the controller. Now I pass these names and I would like to call them back from the component.
I only pass the name of the callback, how to call them from the component?
Thank you!
Have a look at the source code of the SecurityComponent in CakePHP, it has an identical situation with its blackHoleCallback.
They use a helper function SecurityComponent::_callback() which uses PHP's call_user_func_array()
protected function _callback(Controller $controller, $method, $params = array()) {
if (is_callable(array($controller, $method))) {
return call_user_func_array(array(&$controller, $method), empty($params) ? null : $params);
} else {
return null;
}
}
You can use the same pattern to pass callbacks into your component.
The point of a component is re-usability across many controllers - if you're trying to make it access a specific controller function, you should probably just be using controller methods, not a component.
BUT - you can always just do your logic in the component and pass back data (upon success or error), then check the results in the controller, and access whichever method you'd like based on those results.
Since your 'success' or 'error' logic is in the controller, I assume you don't want it in the component... ie it's different per use of the component. In that case, all you really want the component to do is do the logic and let you know how it went (return data).
//Controller
//..
public function facebook(){
$results = $this->Social->auth('facebook', 'success', 'error');
if($results['error']) $this->Social->error();
$this->render(false);
}
private function success(){ }
private function error(){ }
//...
//Component
//...
public function auth($provider, $success, $error){
$results = array();
//do something
$results['error'] = true;
return $results;
}
//...
Related
I am new to Laravel. I have some functions in PaymentController. I want to call those functions from SmartpaySController. Here is the function which is available in PaymentController. Help me to call that function by staying in SmartpaySController.
public function getPaymentFailed($paymentId) {
$transactionData = $this->paymentRepo->find($paymentId);
if($transactionData) {
$data['quote'] = $this->quoteRepo->getQuoteById($transactionData->quote_id);
$data['metaTitle'] = 'Payment failed';
$data['returnMessage'] = $transactionData->return_message;
return view('payment::payment.quote_payment_failed', $data);
}
}
Thank you.
Instead of calling controller methods, the better practice is that you can create traits like: app/Traits and extend in controller
//trait
trait traitName {
public function getData() {
// .....
}
}
//Controller
class ControlelrName extends Controller {
use TraitName;
}
I recomend you to not call functions from one controller to another.
Make Helpers, Resources or implement same feature in other way
Never use controllers as object
But if you want to do it anyway you can use:
SomeController.php
class SomeController extend Controller {
public function someFunction(Request $request) {
// Here Some Code
}
}
YourController.php
use SomeController;
...
public function getPaymentFailed(Request $request, $paymentId) {
$controller_data = (new SomeController)->someFunction($request);
$transactionData = $this->paymentRepo->find($paymentId);
if($transactionData) {
$data['quote'] = $this->quoteRepo->getQuoteById($transactionData->quote_id);
$data['metaTitle'] = 'Payment failed';
$data['returnMessage'] = $transactionData->return_message;
return view('payment::payment.quote_payment_failed', $data);
}
}
Change:
public function getPaymentFailed($paymentId)
to:
public static function getPaymentFailed($paymentId)
This will make it staticly available in your SmartpaySController by doing:
PaymentController::getPaymentFailed($paymentId);
You can make use of Real-Time Facades
Using real-time facades, you may treat any class in your application
as if it were a facade.
To generate a real-time facade, prefix the namespace of the imported
class with Facades:
//...
use use Facades\App\Http\Controllers\SomeController;
//...
return SomeController::getPaymentFailed($request, $paymentId);
Why is it that whenever I redirect something through the constructor of my Codeigniter 4 controller is not working?
<?php namespace App\Controllers\Web\Auth;
class Register extends \App\Controllers\BaseController
{
function __construct()
{
if(session('username')){
return redirect()->to('/dashboard');
}
}
public function index()
{
// return view('welcome_message');
}
}
But if I put it inside index it's working as expected.
public function index()
{
if(session('username')){
return redirect()->to('/dashboard');
}
}
The thing is, I do not want to use it directly inside index because I it need on the other method of the same file.
As per the Codeigniter forum, you can no longer use the redirect method in the constructor to redirect to any of the controllers.
Please refer the below link for more information
https://forum.codeigniter.com/thread-74537.html
It clearly states that redirect() will return a class instance instead of setting a header and you cannot return an instance of another class while instantiating a different class in PHP.
So that's why you can't use redirect method in constructor.
Instead, what I can suggest to you is that use the header method and redirect to your desired controller.
<?php namespace App\Controllers\Web\Auth;
class Register extends \App\Controllers\BaseController
{
function __construct()
{
if(session('username')){
header('Location: /dashboard');
}
}
}
If that's not feasible or difficult to achieve you can follow the below code
<?php namespace App\Controllers\Web\Auth;
class Register extends \App\Controllers\BaseController
{
function __construct()
{
//call to session exists method
$this->is_session_available();
}
private function is_session_available(){
if(session('username')){
return redirect()->to('/dashboard');
}else{
return redirect()->to('/login');
}
}
}
The 2nd solution will be more interactive than the first one. And make sure the method is private. So that it should not be called from other class instances.
The community team has also given a solution to look into the controller filter.
https://codeigniter4.github.io/CodeIgniter4/incoming/filters.html
Please refer to the thread. I hope it may help you in finding a better solution.
In this case you shouldn't even be doing this kind of logic in your controllers. This should be done in a filter and not your controllers.
So you have your controller Register.
You should create a filter in your app/filters folder something like checkLogin.php
That filter should have the following structure:
<?php
namespace App\Filters;
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
class CheckLogin implements FilterInterface
{
/**
* Check loggedIn to redirect page
*/
public function before(RequestInterface $request, $arguments = null)
{
$session = \Config\Services::session();
if (session('username')) {
return redirect()->to('/dashboard');
}
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
// Do something here
}
}
Then in your app/config/Filters.php your should add the filter to the desired controller.
public $aliases = [
'csrf' => \CodeIgniter\Filters\CSRF::class,
'toolbar' => \CodeIgniter\Filters\DebugToolbar::class,
'honeypot' => \CodeIgniter\Filters\Honeypot::class,
'checkLogin' => \App\Filters\CheckLogin::class,
];
// List filter aliases and any before/after uri patterns
public $filters = [
'checkLogin' => ['before' => ['Register']],
];
For more information on filters and how to use then please check the documentation.
https://codeigniter.com/user_guide/incoming/filters.html?highlight=filters
You can then even create filters to your other controllers that would redirect to this one in case the user is not logged in.
Codeigniter 4 using initController() to create constructor.
You can't use redirect() inside __construct() or initController() function.
But you can use $response parameter or $this->response to call redirect in initController() before call another function in controller;
<?php namespace App\Controllers\Web\Auth;
class Register extends \App\Controllers\BaseController
{
public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
{
// Do Not Edit This Line
parent::initController($request, $response, $logger);
if(session('username')){
$response->redirect(base_url('dashboard')); // or use $this->response->redirect(base_url('dashboard'));
}
}
public function index()
{
// return view('welcome_message');
}
}
I just got into CI 4 and i had the same issue as you did, since i've been approaching the login system as with CI 3.
So here is the proper way of doing it right. Enjoy.
Codeigniter 4 do not has any return on Constructor, but you can use like this
public function __construct()
{
if (!session()->has('user_id')) {
header('location:/home');
exit();
}
}
Don't forget to use exit()
But, you better use Filters
Question might be unclear. here's the explanation. Let's say I've:
Route file:
Route::get('testing', 'someController#functionOne');
Route::get('testingtwo', 'someController#functiontwo');
Controller file:
public function functionOne() {
$this->data = generateReallyBigArray();
return redirect('testingtwo');
}
public function funtionTwo() {
// Here $this->data is lost. obviously 'coz this controller file got reinstantiated for #functionTwo
return view('someview', ['data' => $this->data]);
}
$this->data is lost the moment testingtwo is hit. How do I pass this data across different route requests? Or if there're other ways of doing it.
I was thinking of doing this:
public function functionOne() {
$this->data = 'somedata';
return $this->functionTwo();
}
public function funtionTwo() {
// Here $this->data is lost. obviously 'coz this controller file got reinstantiated for #functionTwo
// even this doesn't work. Exception: Method get does not exist
return Route::get('testingtwo', function() {
return view('someview', ['data' => $this->data]);
});
}
use with() to send data through session -
public function functionOne() {
$this->data = 'somedata';
return redirect('testingtwo')->with('data', $this->data);
}
Or you could flash() the data for using on next request.
$request->session()->flash('data', $this->data);
the best way for that is
traits
trait Data{
public function getData() {
// .....
}
}
and in your controllers write
use Data;
you can use traits over controllers
or
You can access your controller method like this:
app('App\Http\Controllers\controllerName')->getDataFunction();
This will work, but it's bad in terms of code organisation (remember to use the right namespace for your ControllerName)
I have TheParentController and the inheritating TheChildController, which should assign $moreData to the template, but the render() method should be called in TheParentController.
Is there any function/service for this case? I expect anything like
$this->get('templating')->assignDataForTemplate('moreData', $moreData);
class TheParentController
{
public function myAction($param1) {
return $this->render('template.html.twig', array(
'someData' => $someData
));
}
}
-
class TheChildController
{
public function myAction($param1) {
// !
// Is there any function like "assignDataForTemplate"?
$this->get('templating')->assignDataForTemplate('moreData', $moreData);
// /!
return parent::myAction($param1);
}
}
I would like to avoid something like
// ...
public function myAction($param1, $moreData = null) {
return $this->render('template.html.twig', array(
'someData' => $someData,
'moreData' => $moreData
));
}
}
As far as I'm aware, there is no such way to do this currently. If you go through the sources, you'll see that calling $templating->render() is actually calling TwigEngine->render().That calls Twig_Template->render() which outputs the template to the client.
I fully understand why you might be using HMVC but I believe this approach might be overcomplicating things for you. If you have common code between controllers - just create a static class which can be called directly. Afterwards move your common logic/code there and call it whenever you need it.
Otherwise, you might need to stick with the code you're trying to avoid (or similar workaround) for now.
You could try something like this, so that the parent is unaware of the child.
<?php
class TheParentController {
public function myAction () {
$data = $this->getMyActionData();
return $this->render('template', $data);
}
protected function getMyActionData () {
return [
'someDefault' => 5
];
}
}
class TheChildController extends TheParentController {
// If using annotation based routing override myAction
// with call to parent function and new #Route tag in doc block
protected function getMyActionData () {
$parentData = parent::getMyActionData();
return array_merge($parentData, [
'childData' => 11
]);
}
}
I'm learning Laravel and there are some things that aren't clear to me.
I've added a method to the BaseController class which will be taking care of my ajax requests.
public function ajaxView($page) {
$view = View::make($page);
if(Request::ajax()) {
$sections = $view->renderSections(); // returns an associative array of 'content', 'head' and 'footer'
return $sections['content']; // this will only return whats in the content section
}
return $view; // just a regular request so return the whole view
}
I have a ProfileController which extends BaseController and I have the following code:
return View::make('profile.user')
->with('user', $user);
I would like to change it to:
return parent::ajaxView('profile.user')
->with('user', $user);
How can I make it so my ajaxView method has the same capabilities as View::make() so that I can make use of ->with()? Is there a way to extend it some how even though ajaxView is a method?
Instead of using with, you can pass the data in as an array:
public function ajaxView($page, $data = [])
{
$view = View::make($page, $data);
if (Request::ajax())
{
$sections = $view->renderSections();
return $sections['content'];
}
return $view;
}
Then in your ProfileController pass in your data:
return $this->ajaxView('profile.user', ['user' => $user]);