When I post to this controller, I get this back as the response: Fatal error: Call to a member function find() on a non-object in /app/Controller/AppController.php on line 26 which probably has to do with using $this->data() explicitly. I was using CakePHP save without form
per a recommendation in there, but since I'm not using a form to send the data (thus not using $this->request->data()), I'd like to know what the replacement is for $this->data() so I can get this working.
My database table is is submissions_favorites.
This is my SubmissionFavorite model:
class SubmissionFavorite extends AppModel {
var $name = 'SubmissionFavorite';
var $belongsTo = array(
'User' => array(
'className' => 'User'
)
);
}
This is AjaxController (what I'm posting to):
class AjaxController extends AppController {
var $layout = 'ajax'; // uses an empty layout
var $autoRender = false; // renders nothing by default
var $uses = 'SubmissionFavorite';
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->loginRedirect = array('controller' => 'home', 'action' => 'index');
$this->Auth->allow('addFavorite');
$this->Auth->flashElement = 'flash_error';
}
public function addFavorite() {
$this->autoRender = false;
$this->data['SubmissionFavorite']['user_id'] = $this->Session->read('Auth.User.id');
$this->data['SubmissionFavorite']['submission_id'] = $_POST['id'];
$this->data['SubmissionFavorite']['when'] = DboSource::expression('NOW()');
$message = array('success' => 'success');
$toReturn = json_encode($message);
if ($this->RequestHandler->isAjax()) {
if ($this->Session->read('Auth.User')) {
$this->SubmissionFavorite->save($this->data);
return $toReturn;
} else {
$login = array('login' => 'Please log in to add favorites');
return json_encode($login);
}
}
}
Line 26 in my AppController is:
protected function getSubmissionCount() {
$totalSubmissions = $this->Submission->find('count');
return $totalSubmissions;
}
Which is totally unrelated to anything else. I didn't even add anything to AppController when I wrote the new method within my AjaxController, so I'm not sure how it's relevant (or why I'm even getting an error in that file).
First, following cake conventions, you should name your model SubmissionsFavorite (note the s between Submission and Favorite). This is because your table name is composed by 2 words, even representing a relationship between 2 other tables.
Also, you can't do $this->Submission->... on AppController without telling cake whatever is "Submission". Take a look at this link to see how to initialize Submission model and use it on AppController: Can I use one model inside of a different model in CakePHP?
Regards.
Try to change all
var $name = 'SubmissionFavorite';
to:
public $name = 'SubmissionFavorite';
Also change: var $uses = 'SubmissionFavorite';
to: public $uses = array ('SubmissionFavorite');
Related
I'm attempting to write a test for a model that has no table but sends an email if the data passes validation in CakePHP 2.
To test I want to assert that some data passes validation and would therefore send an email without actually sending one. For this I am attempting to create a mock method for CakeEmail. However, the test is failing because $useDbConfig hasn't be defined for the mock method:-
Undefined property: Mock_MyModel_7a1fb8d0::$useDbConfig
I assume this is an issue with the model not having a table, but cannot see how to resolve it.
My model looks something like (excluding the validation rules):-
<?php
App::uses('CakeEmail', 'Network/Email');
class MyModel extends AppModel {
public $useTable = false;
public function send($data) {
$this->set($data);
if ($this->validates() === false) {
return false;
} else {
$Email = $this->getEmailer();
$Email->from($data['MyModel']['email_from']);
$Email->to($data['MyModel']['email_to']);
$Email->subject($data['MyModel']['subject']);
$Email->send($data['MyModel']['message']);
}
return true;
}
public function getEmailer() {
return new CakeEmail();
}
}
My test is:-
<?php
class MyModel extends CakeTestCase {
public function setUp() {
parent::setUp();
$this->MyModel = ClassRegistry::init('MyModel');
}
public function testSend() {
$emailer = $this->getMock(
'CakeEmail',
array(
'to',
'emailFormat',
'subject',
'replyTo',
'from',
'template',
'viewVars',
'send'
)
);
$emailer->expects($this->any())->method('send')->will($this->returnValue(true));
$MyModel = $this->getMockForModel('MyModel', array('getEmailer'));
$MyModel->expects($this->once())->method('getEmailer')->will($this->returnValue($emailer));
$data = array(
'MyModel' => array(
'email_to' => 'foo#example.com',
'email_from' => 'bar#example.com',
'subject' => 'Foo bar',
'message' => ''
)
);
$result = $MyModel->send($data);
$this->assertTrue($result);
}
}
Any help would be appreciated. This is the first time I've tried/needed to mock a method in Cake using tests.
Class name should have been MyModelTest rather than MyModel. CakePHP's naming convention needs to be adhered to.
I'm trying to change my database connection depending on who is trying to log in to my page.
What I need is a way to save the correct db name so its accessible by all my controllers.
Using a session would work, but I doubt its safe and/or good practice.
If I could set a variable in the AppController from my AccountsController that would be perfect. but basically any way that enables me to share a variable between all controllers.
In my AccountsController I query a standard database for the correct name. then I use configure::write('CompanyDB', $myDbVar). this work fine for this controller, but I cant use configure::read('CompanyDB') in any other controllers.
In my AppModel i have a construct fucntion that sets the db connection depending on the value inside configure::read('campanyDB') as mentioned before, I need to use configure::write('CompanyDB',$myDbVar) in all my controllers for this to work.
In my Account Model I set the $specific=true. this tells the AppModel that it should use the construct and change the db connection.
class AccountsController extends AppController {
public $helpers = array('Html', 'Form','Js');
public $components = array('RequestHandler');
var $uses = array('User', 'Company');
public $name = 'Accounts';
public $myDbVar='coolbeans';
public function beforeFilter() {
parent::beforeFilter();
Configure::write( 'companyDB',$this->myDbVar);
}
}
class AppModel extends Model {
var $specific = false;
function __construct($id = false, $table = null, $ds = null) {
if ($this->specific) {
// Get saved company/database name
$dbName = Configure::read('companyDB');
// Get common company-specific config (default settings in database.php)
$config = ConnectionManager::getDataSource('companySpecific')->config;
// Set correct database name
$config['database'] = $dbName;
// Add new config to registry
ConnectionManager::drop('companySpecific');
ConnectionManager::create('companySpecific', $config);
// Point model to new config
$this->useDbConfig = 'companySpecific';
}
parent::__construct($id, $table, $ds);
}
}
class Account extends AppModel {
public $primaryKey = '_id';
//var $useDbConfig = 'mongo';
var $specific = true;
.....
}
Probably best bet would be to go with a Configuration file:
Reading and Writing Configuration Files:
http://book.cakephp.org/2.0/en/development/configuration.html#reading-and-writing-configuration-files
The basic idea is, you create a file in your Config/ directory with settings for your app, then, in the bootstrap, you load that config file, which makes any of those variables available anywhere in the app.
Example file: Config/dbconnections.php
<?php
$config = array(
'MyAppDBs' => array(
'company1' => 'connectionName',
'company2' => 'differentConnectionName
)
);
In your bootstrap file:
Configure::load('dbconnections');
Anywhere in your App:
$whatever = Configure::read('MyAppDBs.companyDB');
I think if you do this
configure::write('CompanyDB', $myDbVar);
in your appController then you can access it in any controller using
configure::write('CompanyDB',$myDbVar);
since all controller inherits appController.
Im new to cakePHP and tried to create a simple user registration. Now I'm stuck since two days in the validation of the form. The validation function returns always true and so the form is always saved to the database, even if it is empty.
So here are the model, the controller and the view. Maybe you can see what I did wrong here?
/app/Model/MemberModel.php
<?php
class Member extends AppModel {
var $validate = array(
"username" => array(
"rule" => "alphaNumeric",
"allowEmpty" => false
)
);
}
?>
/app/Controller/MemberController.php
<?php
class MemberController extends AppController {
var $components = array("Security");
var $helpers = array("Html", "Form");
public function index() {
}
public function register() {
if($this->request->is("post")) {
Security::setHash("blowfish");
$this->Member->set($this->request->data);
debug($this->Member->validates());
if($this->Member->save(array("password" => Security::hash($this->request->data["Member"]["password"])))) {
$this->Session->setFlash(__("Saved."));
} else {
$this->Session->setFlash(__("Error: ").$this->validationErrors);
}
}
}
}
?>
/app/View/Member/register.ctp
<?php
echo $this->Form->create("Member");
echo $this->Form->input("username");
echo $this->Form->input("password");
echo $this->Form->end("Register");
?>
Oh, I just realized your problem.
/app/Model/MemberModel.php
should be
/app/Model/Member.php
The controller will look for Member.php, and if it can't find it, it will try the default $validate behavior in AppModel
I've worked with cakePHP in the past and liked the way they built their model system. I want to incorporate their idea of handling validation between extended models.
Here is an example:
class users extends model {
var $validation = array(
"username" => array(
"rule" => "not_empty"
),
"password" => array(
"rule" => "valid_password"
)
);
public function create_user() {
if($this->insert() == true) {
return true;
}
}
}
class model {
public function insert() {
if(isset($this->validation)) {
// Do some validation checks before we insert the value in the database
}
// Continue with the insert in the database
}
}
The problem with the this is that model has no way of getting the validation rules as it's the parent class. Is there a way I can pass the $validation property to the parent class without explicitely passing the validation rules through say the create_user() method as a parameter?
EDIT:
Also, avoiding passing it via the __construct() method to the parent class. Is there another way of doing this which would not cause a lot of extra code within my users class but get the model class to do most of the work (if not all?)
If the instance is a $user, you can simply refer to $this->validation in model::insert().
It would seem that model should also be abstract in this case, preventing instantiation and perhaps confusion.
Create a new abstract method in the model class named: isValid() that each derived class will have to implement, then call that method during the insert() function.
model class:
class model {
abstract protected function isValid();
public function insert() {
if($this->isValid())) { // calls concrete validation function
}
// Continue with the insert in the database
}
}
user class:
class users extends model {
var $validation = array(
"username" => array(
"rule" => "not_empty"
),
"password" => array(
"rule" => "valid_password"
)
);
protected function isValid() {
// perform validation here
foreach ($this->validation) { //return false once failed }
return true;
}
public function create_user() {
if($this->insert() == true) {
return true;
}
}
}
after some questions about how to resolve one of my problem (http://stackoverflow.com/questions/13609611/using-several-modules-in-the-same-view) I have another one.
I made a form in a view helper because I needed to access the result of this form in another controller.
So here is my view helper called QuickSearch.php
class Zend_View_Helper_QuickSearch extends Zend_View_Helper_Abstract
{
public function quickSearch()
{
$form = new Application_Form_QuickSearchForm();
return $form;
}
}
calling QuickSearchForm.php
class Application_Form_QuickSearchForm extends Zend_Form
{
public function init()
{
$this->setMethod('post');
$this->setAction('/search/quicksearch');
$this->addElement('text', 'searchLocation', array(
'label' => 'Location:',
'required' => true,
'filters' => array('StringToUpper')
));
//[some other elements]
$this->addElement('submit', 'submit', array(
'ignore' => true,
'label' => 'Search',
));
}
In my main page view, I call the viewhelper with
echo $this->quickSearch();
Which works, since I have access to my form.
When I submit my form, the /search/quicksearch is called as it's supposed to be but when I try to access the data from the form, it seems that it's empty.
Here is my Search controller (SearchController.php)
class SearchController extends Zend_Controller_Action
{
public function init()
{
/* Initialize action controller here */
}
public function indexAction()
{
// action body
}
public function quicksearchAction()
{
$form = new Application_Form_QuickSearchForm();
if ($this->getRequest()->isPost())
{
echo("post");
$data = $form->getValues();
echo($data['searchLocation']);
}
}
}
I put the echo("post") to see if I was getting a POST request, and every time it works.
the only thing that display is the 'post' and the second echo doesn't display anything.
I don't know what I didn't understand in the way to get the form's data from another controller.
Can someone help me with this ? I just don't know why it doesn't work.
You need to pass the post data to the form object. The typical way to do this is by calling the isValid() method of the form (which also validates the submitted data):
public function quicksearchAction()
{
$form = new Application_Form_QuickSearchForm();
if ($this->getRequest()->isPost()) {
if ($form->isValid($this->getRequest()->getPost()) {
$data = $form->getValues();
echo $data['searchLocation'];
}
}
}