I have my Authentication setup in CakePHP to redirect all users to a dashboard using this function:
function dashboard() {
$group_name = $this->User->Group->field('name', array('id' => $this->Auth->User('group_id')));
$action = $group_name . '/dashboard/';
$this->redirect = array('controller' => 'users', 'action' => $action);
}
My question is what would be the best practice (or resource I can look at) to manage group-specific, and user-specific content within this dashboard
A model method that will fetch all the data you need in a single array with keys named like the view vars you want to use and set it from the controller to the view if you need to fetch different things that are not necessary associated.
If not simply return the data and set it to a view var like this:
public function dashboard() {
$this->set('artist', $this->Artist->dashboard($this->Auth->user('id')));
}
By passing the user id you can fetch whatever you need in the model through the associations.
Related
I'm just finished the intermediate laravel tutorial here: https://laravel.com/docs/5.2/quickstart-intermediate and am trying to push on a bit.
While I can fetch all the tasks via auth'd user id with:
public function index(Request $request)
{
$tasks = $request->user()->tasks()->get();
return view('tasks', [
'tasks' => $tasks,
]);
}
I want to create a view function... how, if I create a link to /task/7 to I query the info about the task with id 7 (for example) & send it to the view?
Define a route to match that /task/7 URL like so:
Route::get("/task/{taskId}", "TaskController#getView");
Then, in your TaskController, define getView function as:
public function getView($taskId){
$task = \App\Task::where("id", "=", $taskId)->first();
return view("tasks.view")->with(["task" => $task]);
}
This is the simplest way to pass a taskId to via a URL parameter, query your DB for the matching record and return a view to display information about it.
There's more involved, like validating the record exists for example, but this should get you on your way.
Brand new to Laravel!
As my title states I would like to be able to immediately redirect from storing a database record to its edit view.
I have a controller called PlannerController. Within this controller I have the standard store() and edit($id) methods. Here is the code for my store() method:
public function store()
{
$newIncome = new Income('fin_income');
$newIncome->user_id = '1';
$newIncome->income_id = Input::get('categories');
$newIncome->income_desc = "Test YO!";
$newIncome->income_date = date('Y-m-d');
$newIncome->amount = Input::get('amount');
$newIncome->save();
return Redirect::route('planner.edit');
}
When I redirect to the planner.edit view (the edit($id) controller method) I have not been able to figure out how to carry over the fin_income.id field, which is auto_incremented upon addition to the database. When I do the redirect like this, my redirect url is /planner/{planner}/edit.
Obviously I need something extra in the store() method but what is the best way to go about this?
have you tried:
return Redirect::route('planner.edit', ['id' => $newIncome->id]);
after you save() on the model, the auto incremented value will be set on your primary key property.
I just started cakephp following there tutorials
I'm able to grab the posts table in my post controller and spew it onto my index.ctp
In my view for the post controller i also want to list the User name that posted the article. My post table has a user_id, so i need to match it to my user table and pass it along
class PostsController extends AppController {
public function index() {
//passes values to the view
$this->set('posts', $this->Post->find('all'));
//is "Post" a post method? or is it the name of the table? i'm unsure of the syntax
$this->set('users', $this->Users->find('all')); //this does not work
}
}
thank you for your help with this basic question
You must use 'recursive'
$this->Post->find('all', array(
'recursive' => 2,
// ...
));
Of course, you first need to link models together
I assume that you have already set a belongsTo association (Post belongsTo User) and/or a hasMany association (User hasMany Post). If so, cake will automaticly brings the associated models (unless you put $recursive = -1 on your model).
Thus you'll have access to the users related to each post on the view: posts[i]['User']
You can also use this on your view to see the view variables:
debug($this->viewVars)
put this on your Post model if you don't:
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
)
);
Make sure that you load models corretly (in case you want to load the User model inside PostsController).
So simply add this attribute inside your class controller.
public $uses = array('Post','User');
to link models together . u need to add the association inside your Post model .
public $belongsTo = array(
'User'=>array(
'className'=> 'User',
'foreignKey'=>'user_id'
)
);
and i you want to retrieve data from database you have to set your recursivity and there is two ways
first one :
$posts = $this->Post->find('all',array('recursive'=>2));
// or
$this->Post->recursive = 2;
$posts = $this->Post->find('all');
second one : use the Containable behavior
set the recursivity to -1 in the AppModel and include the behavior
public $recursive = -1;
public $actsAs = array('Containable');
so simply u can retieve posts with any other linked models like that
$posts = $this->Post->find('all',array(
'contain'=>array('User'),
// ...
)));
I want want to restrict the records based on the user logged in viewing the records.
For example I have following two models:
Assignments
Users
So if a student (user) is viewing the summary of assignments he should be able to see only his assignments and able to perform view/delete/edit only his assignment.
But if a teacher (user) is viewing summary of assignments then he should see all assignments and can perform add/edit/delete on all assignments.
I am already aware I can do this by putting group conditions in find and then appropriate in code in view/edit/delete actions also.
My Question is - what is the best ways to handle scenarios like this in cakephp? Putting conditions everywhere don't seems good way to me.
Consider as two separate problems to solve
Access control
The first is how to deny students who e.g. just manipulate the url to attempt to view/edit/delete things they don't own. For that use isAuthorized, there's an example in the book, adapted to the info in the question that'd be:
// app/Controller/AppController.php
public $components = array(
'Session',
'Auth' => array(
'authorize' => array('Controller') // Added this line
)
);
public function isAuthorized($user) {
// Teachers can access/do everything - adapt to whatever identifies a teacher
if ($user['is_teacher'])) {
return true;
}
// Anyone logged in can access the index
if ($this->action === 'index') {
return true;
}
// The owner of a whatever can view, edit and delete it
$id = $this->request->params['pass'][0];
$owner = $this->{$this->modelClass}->field('user_id', array('id' => $id));
if ($owner === $user['id'])) {
return true;
}
// Default deny
return false;
}
Restrict student data
The event system, available since 2.1, is an easy way to enforce the data restriction mentioned. Again there's a relevant example in the book, adapted to the information in the question, that'd be:
// app/Controller/AssignmentsController.php
public function beforeFilter() {
if (!$this->Auth->user('is_teacher')) {
$currentUser = $this->Auth->user('id');
$this->Assignment->getEventManager()->attach(
function(CakeEvent $e) use ($currentUser) {
$e->data[0]['conditions']['user_id'] = $currentUser;
return $e->data[0];
},
'Model.beforeFind'
);
}
}
This will add a user_id condition to all finds, therefore the index listing will show only their own assignments, whereas for a teacher it will show all assignments.
I would create 2 separate models - StudentAssignments and TeacherAssignments. both models can extend Assignments model. you can then filter your conditions in the beforeFind of each model.
This way, you have decoupled models and you can manipulate them appropriately.
Example:
App::uses('Assignment', 'Model');
class StudentAssignments extends Assignment
{
function beforeFind( $queryData ) {
$queryData['conditions'] = array_merge( (array)$queryData['conditions'],
array( $this->alias.'.user_id' => CakeSession::read('User.id') ) );
return parent::beforeFind($queryData);
}
}
Then you can call $this->StudentAssignments->find('all'); to pull all assignments for a single user
I'm new to cakePHP and MVC development and trying to create something with cakePHP but can't figure out how to do this :
I'm creating a simple CRUD application which takes in Albums and Songs through simple data entry forms. I created the DB and used the Cake console app to create all the models / controllers etc and it works well. I can CRUD both albums and songs no problem, and the song table in the DB is connected to the album table with a foreign key so all the links and associations are there in the model.
What I want to do is be able to click on an album and see the songs associated with that album, ,but I'm not sure how to go about it. Do I need to add a query in the model, or does that functionality go into the Controller ?
My take is : in the album list make the album names links, which call a |viewAlbum| function in the Songs Controller with the albumID. Not sure where to go from here though ......
Can anyone point me in the right direction ?
Cheers,
Colm
#JohnP Thank you for your reply. How do you create a link to call that function in the controller though ? I have :
echo $html->link(__($album['Album']['title'], true),
array('controller'=>'Songs',
'action'=>'viewAlbum',
$album['Album']['id']));
Where viewAlbum is the name of the function in the songs controller. Any ideas on why this doesn't work ?
Protos -
If I understand correctly -- you're using John's example, and you need to fix the link in your view that calls his controller?
<?
echo $this->Html->link(__($album['Album']['title'], true), array('controller'=>'Album', 'action'=>'viewSongs', $id));
?>
John's example explained how to create a method in the Albums controller, suggested hitting a method in the Songs model that returned the desired results.
So your link would target the Album controller, and its action should be the controller method.
This method makes less sense in the Songs controller, because it requires an Album id. You just want the Album controller to pull associated data from the Songs model / table. John's answer is exactly correct but maybe too complicated if you're just getting started with Cake. John split the needed functionality by putting a method in the Song model, called by a method in the Albums controller, which pulls results for your view to display.
I'm switching this to "fat controller," which is easier to follow for short code but less MVC.
You need a hasMany relationship from Albums to Songs - each Album hasMany Songs:
// ../models/album.php
class Album extends AppModel {
var $name = 'Album';
var $hasMany = array(
'Song' => array(
'className' => 'Song',
'foreignKey' => 'album_id'
)
);
Your controller action will look like this:
// ../controllers/albums_controller.php
function viewSongs($id = null) {
if(isset($id) && $id != null) {
$albums = $this->Album->find('first',
array('conditions'=>array('Album.id'=>$id));
$songs = $this->Album->Song->find('all',
array('conditions'=>array('Song.album_id'=>$id)));
// This returns variables to the view to use
$this->set(compact('albums', 'songs'));
}
}
Your view will be called viewSongs.ctp, and it'll look something like this:
// ../views/albums/viewSongs.ctp
<?php
foreach($albums as $album) {
echo "<h2>{$album['name']}</h2>";
echo "<ul>";
foreach ($songs as $song) {
echo "<li>{$song['Song']['name']}</li>"
}
echo "</ul>";
}
And your link in ../views/albums/view.ctp would be like:
<?php
echo $this->Html->link('View Songs', array('controller'=>'albums',
'action'=>'viewSongs', $id));
?>
Cake's native ORM already does this for you. If you actually go into the view page for an album, it should be showing you all the related songs there it self. This works only if you have setup the relationships properly.
If you want to code this behavior yourself, you could put a viewSongs action in your AlbumController. This method would look at the album ID passed to it, and call a method (e.g. getSongsByAlbum($aid)) in your Song model. Inside that method in your song model would be a call that looks something like
$opts = array(
'conditions' => array(
'album_id' => $aid
)
);
return $this->find('all', $opts);