cakePHP - adding new functionality to the model or controller - php

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);

Related

Laravel simplify a database query with relations

I'm just started to work with Laravel and think its a pretty good framework.
But there is a lot to learn and i can mostly find everything in the user guide except this part:
I'm trying to get items from my database they are sorted with a category id that relates to a other table item_catagories in this table are stored:
id
name
parent
In my url of the website I use the name of the category instead of the id.
http://example.com/catagory/subcatagory
when subcatagory has a value I want to search for the related items.
I now have it like this:
if($subcategory){
$foo = ItemCategories::where(['group' => $category, 'name'=> $subcategory])
->get()[0]->id;
$data['products'] = Items::where('category_id', $foo)->get();
}
but there must be a much simpler way to get the same results.
I hope someone can help me to understand how I can do it better
Edit
I forgot to add the relation code:
The item class:
public function categorie(){
return $this->hasOne('App\ItemCategories');
}
The categorie class:
public function items(){
return $this->belongsTo('App\Items');
}
You can use Laravel Eloquent's relationships for this. On your category model:
public function items() { return $this->hasMany('Items'); }
Once you've done that, on a category, you can do $category->items to fetch all of its related items.
Incidentally, when you do this:
->get()[0]
you can just do this:
->first()
If you wish to bring your results already populated, one way to do this is:
$foo = ItemCategories::where(['group' => $category, 'name'=> $subcategory])->with('items')->first();

Laravel PHP, getting clicked link class

I have a html5 table which is dynamically made from database items and it contains links, e.g. delete icon, which is in link tags . I want to be able to click the delete icon and know, which item I want to delete. I have set class of the link the same as the relevant database items ID, but I cant read the class from the controller, after I click the link. Is there a method of doing that in PHP Laravel? Or maybe you could suggest a way better way to accomplish that? This seems a way off tactic for this.
If each row on the table represents a row on database, so your link could contain the id from database.
For example, a user table.
Row 1 => Link /users/delete/1
Row 2 => Link /users/delete/2
Row 3 => Link /users/delete/3
By doing it this way, you can know for sure which one is called.
On your routes file, if you are not using Route::resource(), you should have something like this:
Route::get('users/delete/{id}', 'UsersController#destroy');
And in your destroy method:
public function destroy($id)
{
// your logic here
}
Format your links as:
If for example you are listing all items using foreach:
#foreach( $items as $item )
{{$item->name}}
#endforeach
Inside routes.php
Route::get('item/delete/{id}', 'ItemsController#deleteItem');
inside ItemsController.php define the following function
public function deleteItem($id) {
$item = Item::get($id);
if( !$item ) App::abort(404);
$item->delete();
return Redirect::to('/');
}
and I am assuming you have your model in Item.php
class Item extends Eloquent {
protected $table = 'items';
}
and your items table has id and name columns

Creating new record and relationships in one go

I have the following basic schema:
players
id
name
profiles
id
player_id
email
subsets
id
profile_id
alias
I was under the impression the following operation was possible when creating a new record:
Player::create([
'name' => 'Player 1',
'profile.email' => 'player1#email.com',
'profile.subset.alias' => 'Player 1 alias'
]);
Since this code doesn't seem to work, is there anyway to save relationships records together with the create method?
Basically, you can't do this as easy as it looks.
In the docs, all related models are created after the base model is created
$profile = new Profile(array('email' => 'player1#email.com','alias'=>'Player 1 alias'));
$player = new Player(array('name'=>'Player 1'));
$player = $post->profile()->save($profile);
However , if you really want to do it in one go, you can overwrite the save() method in the Player model :
public function save(){
Database::transaction(function() {
$profileModel = new Profile($this->profile);
parent::save();
$this->profile()->insert($profileModel);
});
}
You will then pass the array to the Player method like :
array(
name='Player name',
profile=>array(
email=>'player1#email.com',
subset=>array(
alias=>'Player 1 alias'
)
);
Although this is not a recommended action.
Please read more about how to save the Eloquent models and relationships :
Tutorial 1
Tutorial 2
Everywhere is suggested to create the base model first, then the related models.

cake php accessing a table

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'),
// ...
)));

List posts by category in CakePHP

I'm new to CakePHP. Please help me to write a function to retrieve posts under a particular category for my blog app built using CakePHP.
My table structure:
posts: id, post, body, created, category_id
category: id, group
Also I had defined:
Inside post model - var $belongsTo = 'Category';
Inside category model - var $hasMany = 'Post';
find() is the generic query method for Models in CakePHP.
An example would be:
$results = $this->Post->find('recursive' => -1, 'conditions' => array('Post.category_id' => 1));
debug($results);
There are many ways to achieve what you want. I encourage you to read the docs or working through the CakePHP Blog Tutorial.
$this->Post->find('all', array('conditions' => array('Post.category_id' => $category_id)));
where $category_id is the id of category that you want to retrieve results from database
hope this helps

Categories