I am new in cakePHP try to create a blog site where user add blog after category select, I have one categories table : fields: category_id, name and posts table : fields : id, category_id , title, body.
I want to fetch all categories to a dropdown list. When user add new post they have to select category first then he is able to post anything..
My PostsController:
<?php
class PostsController extends AppController{
public $helpers = array('Html', 'Form', 'Session');
public $components = array('Session','Paginator');
public function index(){
$this->Paginator->setting = array(
'limit' =>'10',
'order' => array('Post.id' =>'Desc')
/*'conditions' => array(
'Post.user_id' => AuthComponent::user(id)
)*/
);
$arrPosts = $this->Paginator->paginate('Post');
$this->set('posts', $arrPosts);
}
public function view($id = null){
if(!$id){
throw new NotFoundException(__("Error Processing Request ID"));
}
$post =$this->Post->findById($id);
if(!$post){
throw new NotFoundException(__("Error Processing Request POST"));
}
$this->set('post',$post);
}
public function add(){
// HERE I want to fetch all categoryies from categories table and want to send to view
if($this->request->is('post')){
$this->Post->create();
if($this->Post->save($this->request->data)){
$this->Session->setFlash(__('Blog Posted Sucessfully'));
return $this->redirect(array('action' => 'index'));
}else{
$this->Session->setFlash(__('Unable to Post Blog '));
}
}
}
}
?>
I want to show my category in add form:
Please help me ...
In your controller action you need to use $this->set() to set a View variable. As long as you've got your associations setup correctly in your Post model you should then be able to use:-
$this->set('categories', $this->Post->Category->find('list'));
Cake's FormHelper should automatically know that the Post.category_id form field wants to be a select input with $categories as the options.
One further point, it would be better to set the view variables after processing the form as you won't need them if it saves correctly and so can reduce the number of database queries by 1.
If you use find('all') to retrieve the categories you will need to convert it into the format used by find('list') which can easily be done using Hash::combine():-
$categories = $this->Post->Category->find('all');
$this->set(
'categories',
Hash::combine($categories, '{n}.Category.id', '{n}.Category.name')
);
The only real value in doing this is if you need $categories for something else.
public function add(){
/*Here get the category list & set the view (ctp file)*/
$this->set('categories', $this->Post->Category->find('list'));
if($this->request->is('post')){
$this->Post->create();
if($this->Post->save($this->request->data)){
$this->Session->setFlash(__('Blog Posted Sucessfully'));
return $this->redirect(array('action' => 'index'));
}else{
$this->Session->setFlash(__('Unable to Post Blog '));
}
}
}
//Set The dropdown in ctp file
$this->Form->input('category_id', array('type'=>'select', 'options'=>'categories'));
Related
I've created a form which adds a category of product in a Categories table (for example Sugar Products or Beer), and each user has their own category names.
The Categories table has the columns id, category_name, userId, created_At, updated_At.
I've made the validation and every thing is okay. But now I want every user to have a unique category_name. I've created this in phpMyAdmin and made a unique index on (category_name and userId).
So my question is this: when completing the form and let us say that you forgot and enter a category twice... this category exist in the database, and eloquent throws me an error. I want just like in the validation when there is error to redirect me to in my case /dash/warehouse and says dude you are trying to enter one category twice ... please consider it again ... or whatever. I am new in laravel and php, sorry for my language but is important to me to know why is this happens and how i solve this. Look at my controller if you need something more i will give it to you.
class ErpController extends Controller{
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
return view('pages.erp.dash');
}
public function getWarehouse()
{
$welcome = Auth::user()->fName . ' ' . Auth::user()->lName;
$groups = Group::where('userId',Auth::user()->id)->get();
return view('pages.erp.warehouse', compact('welcome','groups'));
}
public function postWarehouse(Request $request)
{
$input = \Input::all();
$rules = array(
'masterCategory' => 'required|min:3|max:80'
);
$v = \Validator::make($input, $rules);
if ($v->passes()) {
$group = new Group;
$group->group = $input['masterCategory'];
$group->userId = Auth::user()->id;
$group->save();
return redirect('dash/warehouse');
} else {
return redirect('dash/warehouse')->withInput()->withErrors($v);
}
}
}
You can make a rule like this:
$rules = array(
'category_name' => 'unique:categories,category_name'
);
I'd like to ask, is it possible to change the original posted attributes in actionCreate()?
For example I have 3 attributes: name, phNumber, address
In the _form.php, it automatically posts these 3 attributes. BUT what if I want to change the posted name attribute to all Uppercases? Do I need to create my own method of creating a record just to change how the name will be recorded OR is there something that I can do in actionCreate() so that it only changes the name attribute?
For example, user types in
adam michael
for the name textbox, and I want to change only this attribute to
ADAM MICHAEL
to be recorded in the database instead of having to create another method.
Code below:
public function actionCreate() {
$model = new Masseuse;
if (isset($_POST['Masseuse'])) {
$model->setAttributes($_POST['Masseuse']);
if ($model->save()) {
if (Yii::app()->getRequest()->getIsAjaxRequest())
Yii::app()->end();
else
$this->redirect(array('servicemasseuse/create', 'mid' => $model->id));
}
}
$this->render('create', array( 'model' => $model));
}
Just simply do a $model->name=strtoupper($model->name);
Refer here
You must alter the user input prior to saving the data. You do this by creating an overwritten function in your model.
class Masseuse extends CActiveRecord {
// ...
public function beforeSave()
{
$this->name = strtoupper($this->name)
}
}
I'm attempting to build an achievements system in my CakePHP app using CakeEvents. I've been using the following website for helping me put the Events together: http://martinbean.co.uk/blog/2013/11/22/getting-to-grips-with-cakephps-events-system/
In my app, achievements are called badges and can be awarded to users. These badges are awarded based on rules that link the badge to a Cake event.
So for example if a user creates a post, that will fire the Model.Post.add event which should check if any rules exist for that event, and if so do the parameters match up, and again if all checks out then award a badge connected to that rule to the user.
The schema comprises of the following tables:
users
id
username
password
posts
id
title
content
created
modified
user_id
badges
id
title
description
events
id
title
name
event_badges
id
event_id
badge_id
badge_users
id
badge_id
user_id
Hopefully that all makes sense. And here are the Models to show how they connect.
User.php
class User extends AppModel
{
public $name = 'User';
public $hasMany = array(
'Post', 'Badge'
);
public $belongsTo = array(
'BadgeUser'
);
}
Badge.php
class Badge extends AppModel {
public $hasMany = array(
'BadgeUser'
);
public $actsAs = array('Containable');
}
Event.php
class Event extends AppModel {
public $hasMany = array(
'EventBadge'
);
public $actsAs = array('Containable');
}
EventBadge.php
class ActionBadge extends AppModel {
public $belongsTo = array(
'Action', 'Badge'
);
public $actsAs = array('Containable');
}
BadgeUser.php
class BadgeUser extends AppModel {
public $belongsTo = array(
'Badge', 'User'
);
public $actsAs = array('Containable');
}
** Feel free to comment if you think the schema is incorrect to achieve what is being described in this question.**
So example badges might be:
Title: First Post, Description: Awarded for creating a post
Title: 10 Posts, Description: Awarded for creating 10 posts
Title: Editor, Description: Editing a post
Title: Deleter, Description: Deleting a post
And some example rules in the Events table:
Title: Add Post, Event Model.Post.add
Title: Edit Post, Event: Model.Post.edit
Title: Delete Post, Event: Model.Post.delete
Title: View Post, Event: Model.Post.view
So as you can see the Events are linked to the above Badges and the Events are called using the CakeEvents system.
Okay so when a person does something, let's say saves a new post, I have the following in the Post model:
public function afterSave($created, $options = array()) {
if ($created) {
$event = new CakeEvent('Model.Post.add', $this, array(
'id' => $this->id,
'data' => $this->data[$this->alias]
));
$this->getEventManager()->dispatch($event);
}
}
The first question is, how do I pass different events to the afterSave? As both Add and Edit methods in the controller would fire this...
And then I have a PostListener.php file in /app/Event
class PostListener implements CakeEventListener {
public function implementedEvents() {
return array(
'Model.Post.add' => 'postAdded',
'Model.Post.edit' => 'postEdited',
'Model.Post.view' => 'postViewed',
'Model.Post.delete' => 'postDeleted',
);
}
public function postAdded(CakeEvent $event) {
$this->Badge->awardBadge($badgeId, $userId);
}
public function postEdited(CakeEvent $event) {
}
public function postViewed(CakeEvent $event) {
}
public function postDeleted(CakeEvent $event) {
}
}
So the next question is how do I link the event listener back up to my Events table?
And then award the badge connected to that action? Noting that some will need to do extra checks like a user must of created 10 posts to achieve the 10 Posts badged and not just because they have created a post.
In the Badge.php model I have the following function to award badges:
public function awardBadge($badgeId, $userId) {
$controlFind = $this->BadgeUser->find(
'first',
array(
'conditions' => array(
'badge_id' => $badgeId,
'user_id' => $userId,
)
)
);
if(!$controlFind) {
$temp = array(
'BadgeUser' => array(
'badge_id' => $badgeId,
'user_id' => $userId,
'created' => date('Y-m-d H:i:s')
)
);
$collection[] = $temp;
return $this->BadgeUser->saveAll($collection, array('validate' => false));
}
}
So I need to run the code above from the listener when things match up with the DB rules. Just struggling to make it all stick together.
I think your database schema may be complicating things by having a notion of events. Personally, I’d have a badges table that stores badge title and description (and also image path if it’s needed). I’d then have an event listener that corresponds to each badge, so there will be some degree of manual labour involved.
So let’s think of a sample scenario. Let’s say a badge is awarded when a user posts a 100 times. So you’d have your Post model that fires an event when a post is saved:
<?php
App::uses('CakeEvent', 'Event');
class Post extends AppModel {
public function afterSave($created, $options = array()) {
$event = new CakeEvent('Model.User.afterSave', $this);
$this->getEventManager()->dispatch($event);
}
}
You can then create a corresponding handler:
<?php
// Event/BadgeListener.php
App::uses('CakeEventListener', 'Event');
App::uses('ClassRegistry', 'Utility');
class BadgeListener implements CakeEventListener {
public function implementedEvents() {
return array(
'Model.Post.afterSave' => 'afterSaveListener'
);
}
public function afterSaveListener(CakeEvent $event) {
// check number of posts
$this->Post = ClassRegistry::init('Post');
$count = $this->Post->find('count', array(
'Post.author_id' => $event->subject()->data[$event->subject()->alias]['author_id'];
));
// award badge
if ($count > 100) {
// TODO: check user does not already have badge
$this->BadgeUser = ClassRegistry::init('BadgeUser');
$this->BadgeUser->create();
$this->BadgeUser->set('badge_id', 'whatever_badge_id_actually_is');
$this->BadgeUser->set('user_id', $this->Auth->user('id')); // logged in user ID
$this->BadgeUser->save();
}
}
}
This is a rough example written off-the-cuff, but hopefully it should steer you in the right direction.
If there’s anything you want me to clear up, let me know and I’ll do my best.
The first question - differentiate between new post and edit
To differentiate between add and edit you need to pass along the $created parameter when you create your CakeEvent. Something like this:
public function afterSave($created, $options = array()) {
//NOTICE: it's afterSave now, since this will be called after edit or new
$event = new CakeEvent('Model.Post.afterSave', $this, array(
'created' => $created, //NOW you will know when you process the even if this was a new post or post edit
'id' => $this->id,
'data' => $this->data[$this->alias]
));
$this->getEventManager()->dispatch($event);
}
You should add to the data of your event all the data that you will need later on to process it and to match your rules. You can even add the model object itself, if you think you will need that to do some queries with it.
Second Question - how to link event processing to the Events table
In the Event listener you can use the ClassRegistry to get an instance of the Event model (and any other models you require). You can do your rules matching and once you have all the data you need you save the badge to the database.
Since you now know if it's an edit or a new post (from the event->data) you can take appropriate action.
The editor id I guess you can take it with the Auth component as being the logged in user. So when you have that you can use it to read what you need about the editor from the database.
To conclude: when you send your events, use the event data to pass all the information that you will need in the listener. Event can carry so much more information, not just their names and the fact that they were triggered. You can send and then look at the entire "context".
In the listener, make good use of the data you get from the event and of the currently logged in user ID or the post author, do the matching that you need to do and then save the badge for the appropriate user.
Hopefully this is what you were looking for :).
I'm setting up pagination to display a list of images belonging to the user in their account. This is what I have in my controller:
class UsersController extends AppController {
public $paginate = array(
'limit' => 5,
'order' => array(
'Image.uploaded' => 'DESC'
)
);
// ...
public function images() {
$this->set('title_for_layout', 'Your images');
$albums = $this->Album->find('all', array(
'conditions' => array('Album.user_id' => $this->Auth->user('id'))
));
$this->set('albums', $albums);
// Grab the users images
$options['userID'] = $this->Auth->user('id');
$images = $this->paginate('Image');
$this->set('images', $images);
}
// ...
}
It works, but before I implemented this pagination I had a custom method in my Image model to grab the users images. Here it is:
public function getImages($options) {
$params = array('conditions' => array());
// Specific user
if (!empty($options['userID'])) {
array_push($params['conditions'], array('Image.user_id' => $options['userID']));
}
// Specific album
if (!empty($options['albumHash'])) {
array_push($params['conditions'], array('Album.hash' => $options['albumHash']));
}
// Order of images
$params['order'] = 'Image.uploaded DESC';
if (!empty($options['order'])) {
$params['order'] = $options['order'];
}
return $this->find('all', $params);
}
Is there a way I can use this getImages() method instead of the default paginate()? The closest thing I can find in the documentation is "Custom Query Pagination" but I don't want to write my own queries, I just want to use the getImages() method. Hopefully I can do that.
Cheers.
Yes.
//controller
$opts['userID'] = $this->Auth->user('id');
$opts['paginate'] = true;
$paginateOpts = $this->Image->getImages($opts);
$this->paginate = $paginateOpts;
$images = $this->paginate('Image');
//model
if(!empty($opts['paginate'])) {
return $params;
} else {
return $this->find('all', $params);
}
Explanation:
Basically, you just add another parameter (I usually just call it "paginate"), and if it's true in the model, instead of passing back the results of the find, you pass back your dynamically created parameters - which you then use to do the paginate in the controller.
This lets you continue to keep all your model/database logic within the model, and just utilize the controller to do the pagination after the model builds all the complicated parameters based on the options you send it.
i'am a cakephp newbie :D
how can i modify data in a controller before cakephp put the data into mysql?
function add() {
if (!empty($this->data)) {
$this->Template->create();
/* This works! */
$this->data['Template']['slug'] = Inflector::slug(utf8_encode(strtolower($this->data['Template']['name'])),'-');
/* does not work ! */
$this->data['Template']['created'] = time();
$this->data['Template']['category_id'] = $this->data['Template']['category'];
if ($this->Template->save($this->data)) {
$this->Session->setFlash('Your post has been saved.');
$this->redirect(array('action' => 'index'));
}
}else{
/* dropdown */
$this->set('categories',$this->Template->Category->find('list'));
}
}
Fields in my database:
templates
id
slug
category_id (belong to categories)
name
created
Can anyone help my?
greetings!
The correct way is putting it in your Model, not in your controller (because you are treating data, so it must be in the model).
For this, you can use model's "beforeSave" method:
Cake1.2: http://book.cakephp.org/view/683/beforeSave
Cake1.3: http://book.cakephp.org/view/1052/beforeSave
Cake 2: http://book.cakephp.org/2.0/en/models/callback-methods.html#beforesave