I am using cakephp-2.x. I have one function name user_info() in the UsersController.php i want to access this in another controller name MessagesController.php
Code -
UsersController.php
public function user_info(){
$user_id=$this->Session->read('Auth.User.id');
$data=$this->User->findById($user_id);
$this->set('user_info',$data);
}
MessagesController.php
public function index(){
//$userInfo=new UsersController();
//$userInfo->user_info();
$this->user_info();
pr($data);
}
Error Message-
Fatal Error
Error: Call to undefined method MessagesController::user_info()
File: E:\xampp\htdocs\2014\myshowcam\msc\app\Controller\MessagesController.php
Line: 18
Notice: If you want to customize this error message, create app\View\Errors\fatal_error.ctp
Typically if you're trying to access a function in one controller from another controller you have a fundamental flaw in your project's logic.
But in general object usage is thus:
$otherController = new whateverMyControllerNameIs();
$otherController->functionName();
However I'm not familiar enough with cake to tell you the pitfalls of doing such a thing. For example I have no idea what this would do to routes or what other variables/objects are required to initialize a controller correctly.
EDIT:
Ref: CakePHP 2.3.8: Calling Another Controller function in CronController.php
App::import('Controller', 'Products'); // mention at top
// Instantiation // mention within cron function
$Products = new ProductsController;
// Call a method from
$Products->ControllerFunction();
Try requestAction function of cakephp
$result = $this->requestAction(array('controller' => 'users', 'action' => 'user_info'));
Why would a simple, When can complicated?
All the information for a registered user of User model is accessible in the following manner:
AppController.php
public $user_info; /* global scope */
public function beforeFilter(){
$this->user_info = $this->Auth->user(); // for access user data in any controller
$this->set('user_info_view',$this->Auth->user()); // for access user data in any view or layout
}
MessagesController.php
public function index(){
debug($this->user_info);
$my_messages = $this->Message->find('all',
array('conditions' => array('Message.user_id' => $this->user_info['id']))
}
....
layout or view.ctp
<?php echo $user_info_view['name']; ?> // email, etc
Why not take advantage of the way CakePHP handles relationships? There's a very easy way to achieve what you're trying to do without extending controllers or loading in additional controllers which seems excessive for your example.
Inside AppController's beforeFilter()
Configure::write('UserId', $this->Session->read('Auth.User.id'));
This will allow you to access the UserID from your models
Inside your User's model, create the following function
/**
* Sample query which can be expanded upon, adding fields or contains.
*
* #return array The user data if found
*/
public function findByUserId() {
$user = $this->find('first', array(
'conditions' => array(
'User.id' => Configure::read('UserId')
)
));
return $user;
}
Inside your Users controller (Minimal is better, no?)
public function user_info() {
$this->set('user', $this->User->findByUserId());
}
Inside your Messages controller
public function index() {
$this->set('user', $this->Message->User->findByUserId());
// --- Some more stuff here ---
}
And that's it, no need to be extending controllers, just make sure your Message and User model are related to each other, failing that you can bindModel or use ClassRegistry::init('User')-> for example.
Related
I'm new of Laravel and I have started my first project (with Laravel 5.7).
I have some variables that I would like to use in every single view.
In general I create, for example, a config.php file where I put my variables and use them in every pages (obviusly including config.php in all pages).
But, with Laravel, where can I put this variables? And how can I do to use them in all views?
This is my web.php:
Route::get('/task','TaskController#index');
Route::get('/task/insert','TaskController#setInsertTask');
Route::get('/task/list','TaskController#getTaskList');
And in the TaskController:
class TaskController extends Controller
{
public function index(){
return view('task.index');
}
public function setInsertTask(){
return view('task.insert');
}
public function getTaskList(){
return view('task.list');
}
}
Now I have tried to put the variables in the TaskController like this:
class TicketController extends Controller
{
private $titlePage1 = "Task manager";
private $titlePage2 = "Task manager insert";
public function index(){
return view('task.index',[
'titlePage' => $this->titlePage1
]);
}
public function setInsertTask(){
return view('task.insert',[
'titlePage' => $this->titlePage2
]);
}
public function getTaskList(){
return view('task.list',[
'titlePage' => $this->titlePage1
]);
}
}
And in the view I have insert something like this:
#extends('layout.layout')
#section('content')
<h1>{{ $titlePage }}</h1>
#endsection
But I don't think that is the best solution and I don't like it.
In this project I would like to use this variable beacuse:
1. I would like to managed three different software related each other (ex. Login for users, login for admin, login for technicians) thet they have the same database and the single area is small. So, for each area I'll liked to print a different title.
2. In the pages there are some static word, so I will create an array with all words in such a way to concenter all static words.
3. Like the title page, I would like the same things with a menu. Different menus for different areas managed in a single file in php (not in the html).
4. The same variables I will like to use them in other controllers.
I have searched a lot but I can't find which is the best practise to include a general variable in some views.
Can anyone help me? Thanks a lot.
You can share variables for all views with View::share in AppServiceProvider
I had answered in another question. For details visit this: link
Yes, you can use the variables defined in the .env at route
for example in .env
name=test
You can get it as env('name')
read here
You can do it using BaseController
class BaseController extends Controller
{
public function __construct()
{
$titlePage1 = "Task manager";
$titlePage2 = "Task manager insert";
View::share(['titlePage1' => $titlePage1, 'titlePage2' => $titlePage2 ]);
}
}
You can access it in any view {{$titlePage1}} and {{$titlePage2}}
You can also perform same thing with AppServiceProvider
In boot() of AppServiceProvider, add following code.
public function boot() {
$titlePage1 = "Task manager";
$titlePage2 = "Task manager insert";
View::share(['titlePage1' => $titlePage1, 'titlePage2' => $titlePage2 ]);
}
I have a data that I want to return in all of my controller methods and I thought that'd be cleaner to return from the controller's constructor; because currently it's just repetitive return code.
protected $data;
public function __construct() {
$this->data = "Test";
}
public function index() {
// Stuff
return view('test')->with([
'testData' => $this->data
// other view data
]);
}
public function store() {
// Stuff
return redirect()->back()->with([
'testData' => $this->data
// other view data
]);
}
This is just a pseudo example.
Yes, that's possible. It's done exactly in the same manner you showed.
However, I don't think that's the best way to do it. You might want to take a look into ViewComposers, which help provide a data set to a view (or multiple views) right after the controller but before the view is finally provided to the user.
You could just write a controller method to append the data property for you:
protected function view($name, $data = [])
{
return view($name, $data + $this->data);
}
public function index() {
...
return $this->view('view', ['other' => 'data']);
}
You can use ViewComposer which allow you to attach data to the view every time certain views is rendered
namespace App\ViewComposers;
class DataComposer
{
protected $data = ['1', '2', '3']; // This data is just for sample purpose
public function compose(View $view)
{
$view->with('data', $this->data);
}
}
And to register this composer with the list of all views on which the data must by attached add this code in the boot method off the AppServiceProvider
View::composer(
['view1', 'view2', 'view3', '....'],
'App\ViewComposers\DataComposer'
);
create a after middleware However, this middleware would perform its task after the request is handled by the application
Yes, it is very much possible with the code you have
[EDIT]
If you wish to remove ['testData' => $this->data] from all controller methods, then you can use view composers.
View composers are linked to one view. So for that view, if you have the same set of data to be passed all the time, use view composers!
Thanks for watching my first question.
I have something confused.
How could I write the operations of database into database and don't write the function in every Controller?
I have considered middleware and find that must change my route register style.
my Route is this:
Route:resource('province','\\Modules\\Info\\Controllers\\P_ProvinceController');
Dose it has some awesome methods replace this?
public function Store(Request $request)
{
$params = $request->input('data');
$params['CreateID'] = Auth::user()->id;
$params['CreateName'] = Auth::user()->name;
$params['CreateTime'] = Carbon::now();
$province = P_ProvinceModel::Create($params);
$params['Pro_Is_Del'] = 1;
$log_info['table'] = $province->getTable();
$log_info['type'] = "create";
$log_info['user'] = Auth::user()->name;
$log_info['datetime'] = Carbon::now();
LogModel::create($log_info);
if($province){
return response()->json(array(
'status' => 200,
'msg' => '新增成功',
'data' => $province
));
}else
return response()->json(array(
'status' => 500,
'msg' => '保存失败',
));
}
Thanks.
Here is how I solved across model functionality
First Create a Trait that does what you want on save.
<?php
namespace App\Models\Observers;
trait CreatedByObserver
{
public static function bootCreatedByObserver(){
/** Simply means that whenever this model is creating a model do: */
static::creating(function($model){
if(auth()->check()){
$responsiblePerson = auth()->user()->first_name . " " . auth()->user()->last_name;
} else {
$responsiblePerson = "system";
}
/** You can set any model variables within */
$model->created_by = $responsiblePerson;
});
}
}
In there do all you need to do when a record is saved/created/updated/deleted
Then In all Models you want this behaviour used add the trait.
Check them out here : https://laravel.com/docs/5.2/eloquent#events
As far i understand your question, you are asking for way to make your controller an abstract type, i.e. controller just need to handle the route and view not any other things(like database,application logic etc) which is the philosophy of the laravel Framework.
To make your controller abstract (meaning of abstract as explained aboave),
first you need to understand, "What your application logic are and what your database logic are ?"
when you understand these two things then, you can easily separate your aapplication logic and databasse logic from your controller.
For example :
For keeping your Application logic you can make service folder in your root of your project also you can make folder name 'Dao' (Database access object) in the same path of service . You need to keep these folder in autoload from your composer. Just make class for service and your Dao.
And now your application follow will be,
First Route, will hit controller then controller will need to call some method in service and then service will call the respective DAO . method.
Example :
Controller/YourController.php
Class YourController extends Controller {
public function Store(Request $request,yourservice,$yourService)
{
$this->myservice = $yourservice;
$this->myservice->store('your inputs request');
return $something ;
}
}
service/yourService.php
Class yourService {
public function store($yourinputs,yourDao $mydao){
$this->mydao = $mydao;
//you can use your application logic here
return $this->mydao->create($yourinputs);
}
And now the turn is for DAO :
dao/yourdao.php
use model // use your model here .
class yourDao {
public function create($yourdata,yourmodel $model){
$this->model = $model;
return $this->model->create($yourdata);
}
}
Now, you can see the controller just save the data in database, but don't know how it is saving the data and what are the application logic.
This explanation is just a simple way of doing project to make a controller abstract. There are other various ways of doing this. For example you can see Repository Design Pattern , which also used by laravel core .
Hope this explanation will not bore anyone . :) Happy coding .
I have the following in my controller:
public function actionIndex() {
$userID = Yii::app()->user->getId();
$arNotifs = Notification::model()->getNotificationsByUserId($userID);
$this->render('index', array("arNotifications"=>$arNotifs, "userID"=>$userID));
}
I have the following in a file called notification.php in my models:
class Notification extends CActiveRecord {
// ------ BUNCH OF STUFF
public function getNotificationsByUserId($userId) {
$userId = (int) $userId;
$query = Yii::app()->db->createCommand();
$query->select('n.id, n.title, n.content, n.updated');
$query->from('hr4_notification_x_user nxu');
$query->join('hr4_notification n', 'nxu.notification = n.id');
$query->where('nxu.user=:userId', array(':userId' => $userId);
return $query->queryAll();
}
// ------ MORE STUFF
}
When I rem out the line
$arNotifs = Notification::model()->getNotificationsByUserId($userID);
and replace it with a static value it works fine. It seems that in my noob ways I am missing some vital step. The controller seems to have no idea what Notification is.
Thanks in advance
I believe the most elegant way to get your notifications on the controller would be something like:
$arNotifs = Yii::app()->user->model->notifications;
To achieve such, you might need to implement a getModel() method on your class that extends CWebUser. That method would return an instance of an user that extends CActiveRecord. Then your user model can have a relations() method, like the following:
class UserModel extends CActiveRecord {
public function relations() {
return array(
'notifications' => array(self::HAS_MANY, 'Notification', 'user'),
);
}
}
This will prevent you from writing that query and will make things more clear (on both, models and controller). If you will, read a bit about relations.
You cannot use the Notification model like this.
You can instantiate it with $notification = new Notification();
and then do a $notification->getNotificationsByUserId($userID);
However this would be not very good. I would move the notification code from the model to the User model.
This was you dont even need to pass the user ID.
Or maybe even better, if you make a component out of Notification and use it as a service.
I'm trying to load a blade view with layout, but I get this error:
"Attempt to assign property of non-object"
The structure is the following:
Route:
Route::pattern('controller', '\w+');
Route::get('{controller}', function($controller) {
$controllerClass = $controller.'Controller';
App::make($controllerClass)->index();
});
Controller:
class PricesController extends BaseController {
protected $layout = 'layouts.master';
public function index()
{
$this->layout->content = View::make('prices.index');
}
}
The debug says the issue is at line $this->layout->content = View::make('prices.index');
The views are fine... I have layouts folder with master.blade.php and I also have prices folder with index.blade.php.
The content section is exists as well with #stop and the #yield is there in the layout.
In the BaseController there is the setupLayout method:
protected function setupLayout()
{
if ( ! is_null($this->layout))
{
$this->layout = View::make($this->layout);
}
}
What is the problem? Why I get that exception?
Thank you!
I know I helped you in the #laravel irc channel but there are 3 things here for any others with this problem.
This is not a good use of route files. Controller implicit routing is hard to maintain if your app gets larger. Consider using Route::resource instead if you're just trying to save a few lines of code. But I'll give you the benefit of the doubt.
You'll want to use the nest method for your layout, i.e. $this->layout->nest('content', 'prices.index');
The setupLayout() function is not being called because you are calling index() directly on the object. This is not how Laravel normally processes controllers.
I'm not going to walk through the entire routing process but if you look at vendors/laravel/framework/src/Illuminate/Routing/ControllerDisplatcher.php on line 89 you will see:
protected function call($instance, $route, $method)
{
$parameters = $route->parametersWithoutNulls();
return $instance->callAction($method, $parameters);
}
Let's look at vendors/laravel/framework/src/Illuminate/Routing/Controller.php on line 227 and you will see:
public function callAction($method, $parameters)
{
$this->setupLayout();
$response = call_user_func_array(array($this, $method), $parameters);
... irrelevant stuff omitted ...
}
These reason I show these things is show the magic Laravel is doing behind the scenes.
Basically you are skipping that magic and just calling PricesController->index() directly instead of going through the router. This is why setupLayout is never being called and you will get an exception because there is no $this->layout object yet created.