Can't submit form using create(); - cakephp

So i have this method inside of my:
JobsController.ctp:
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\ORM\TableRegistry;
/**
* Jobs Controller
*
* #property \App\Model\Table\JobsTable $Jobs
*/
class JobsController extends AppController
{
public $name = 'Jobs';
public function add()
{
//Some Vars assigning skipped, var job is empty
$this->set('job','Job');
$this->Job->create();
}
}
And I have this view with the form itself:
add.ctp:
<?= $this->Form->create($job); ?>
<fieldset>
<legend><?= __('Add Job Listing'); ?></legend>
<?php
echo $this->Form->input('title');
echo $this->Form->input('company_name');
echo $this->Form->input('category_id',array(
'type' => 'select',
'options' => $categories,
'empty' => 'Select Category'
));
echo $this->Form->input('type_id',array(
'type' => 'select',
'options' => $types,
'empty' => 'Select Type'
));
echo $this->Form->input('description', array('type' => 'textarea'));
echo $this->Form->input('city');
echo $this->Form->input('contact_email');
?>
</fieldset>
<?php
echo $this->Form->button('Add');
$this->Form->end();
?>
Also this table class:
JobsTable.php
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class JobsTable extends Table
{
public function initialize(array $config)
{
$this->belongsTo('Types', [
'foreignKey' => 'type_id',
'joinType' => 'INNER',
]);
$this->belongsTo('Categories', [
'foreignKey' => 'category_id',
'joinType' => 'INNER',
]);
}
}
And when I submit it, it gives me next error:
Error: Call to a member function create() on boolean
No idea how to fix.
I also have an entity
Job.php:
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
/**
* Job Entity.
*/
class Job extends Entity
{
/**
* Fields that can be mass assigned using newEntity() or patchEntity().
*
* #var array
*/
protected $_accessible = array(
'category_id' => true,
'user_id' => true,
'type_id' => true,
'company_name' => true,
'title' => true,
'description' => true,
'city' => true,
'contact_email' => true,
'category' => true,
'user' => true,
'type' => true,
);
}
So how do I fix this error, that appears on form submit?
Error: Call to a member function create() on boolean
I guess I need to do something with $this->set('job'); ? but I'm not sure what exactly

By convention the default, auto-loadable table for a controller is based on the controller name without the trailing Controller, so for JobsController a table class named Jobs(Table) can be autoloaded.
In case the table class cannot be loaded (for example because it doesn't exist, or because the name doesn't match the one derived from the controller name), the magic getter that handles this will return false, a boolean, and this is where you are trying to call a method on, hence the error.
create() btw doesn't exist anymore, you should have a look at the ORM migration guide, and the docs in general to get a grasp on how things now work.
So either use $this->Jobs and make sure that you have a table class named JobsTable, or override the default model to use (Controller::_setModelClass()), or load the desired table manually (TableRegistry::get() or Controller::loadModel()).
See also
Cookbook > Database Access & ORM
Cookbook > Controllers > Loading Additional Models

Related

CakePHP 3 Model Form Send Email with User submitted attachment

These are the results from when I do debug($this->request->data)
I ommited the fields that didn't matter
I get this
[
'file' => 'Atest.doc'
]
I want this
[
'file' => [
'tmp_name' => '/Applications/MAMP/tmp/php/phpgBEFye',
'error' => (int) 0,
'name' => 'Atest.doc',
'type' => 'application/msword',
'size' => (int) 45056
]
Here is .ctp
echo $this->Form->create($contact, ['id'=>'contact-form']);
echo $this->Form->file('file', ['label'=> 'Cover Letter']);
echo $this->Form->button('Send');
echo $this->Form->end();
My Contact Controller
<?php
namespace App\Controller;
use App\Controller\AppController;
use App\Form\ContactForm;
use Cake\ORM\TableRegistry;
use Cake\Mailer\Email;
use Cake\Network\Exception\SocketException;
use Burzum\FileStorage\Storage\StorageManager;
public function index($option = 'index'){
$contact = new ContactForm();
if ($this->request->is('post')) {
if ($contact->execute($this->request->getData())) {
//debug($this->request->getData() );
}
}
///..I omitted the rest
}
echo $this->Form->create($document, ['enctype' => 'multipart/form-data']);

Applying sessions in cakephp 3.2

Im using cakephp 3.2 to build an application. Im using the bookmarks tutorial as a basis for my project. in one of my bookmarks .ctp view files I would like to have a number of select boxes with data specific to the user loggged in. i have two tables namely users and bookmarks. My bookmarks table contains foreign key from users table user_id.
Here's my bookmark table with the fields i would like the dropdowns. id, user_id, title, systemregistration, systemroles, country, province, metropolitan.
Code for my appcontroller
namespace App\Controller;
use Cake\Controller\Controller;
use Cake\Event\Event;
/**
* Application Controller
*
* Add your application-wide methods in the class below, your controllers
* will inherit them.
*
* #link http://book.cakephp.org/3.0/en/controllers.html#the-app-controller
*/
class AppController extends Controller
{
/**
* Initialization hook method.
*
* Use this method to add common initialization code like loading components.
*
* e.g. `$this->loadComponent('Security');`
*
* #return void
*/
/*public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
}*/
public function initialize()
{
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'email',
'password' => 'password'
]
]
],
'loginAction' => [
'controller' => 'Users',
'action' => 'login'
],
//'storage' => 'Session'
'Session'
]);
// Allow the display action so our pages controller
// continues to work.
$this->Auth->allow(['display']);
}
/*public function initialize()
{
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'loginRedirect' => [
'controller' => 'Bookmarks',
'action' => 'index'
],
'logoutRedirect' => [
'controller' => 'Pages',
'action' => 'display',
'home'
]
]);
}
public function beforeFilter(Event $event)
{
$this->Auth->allow(['index', 'view', 'display']);
}*/
/**
* Before render callback.
*
* #param \Cake\Event\Event $event The beforeRender event.
* #return void
*/
public function beforeRender(Event $event)
{
if (!array_key_exists('_serialize', $this->viewVars) &&
in_array($this->response->type(), ['application/json', 'application/xml'])
) {
$this->set('_serialize', true);
}
}
}
//BookmarksController looks like this
namespace App\Controller;
use App\Controller\AppController;
/**
* Bookmarks Controller
*
* #property \App\Model\Table\BookmarksTable $Bookmarks
*/
class BookmarksController extends AppController
{
public function internalprotocol()
{
$bookmark = $this->Bookmarks->newEntity();
$users = $this->Bookmarks->Users->find('list', ['limit' => 200]);
$tags = $this->Bookmarks->Tags->find('list', ['limit' => 200]);
$this->set(compact('bookmark', 'users', 'tags'));
$this->set('_serialize', ['bookmark']);
$bookmarks = $this->paginate($this->Bookmarks);
$this->set(compact('bookmarks'));
$this->set('_serialize', ['bookmarks']);
}
}
//my internalprotocol.ctp looks like this
<div>
<?php echo $this->Form->input('user_id', ['options' => $bookmarks]); ?>
<?php echo $this->Form->input('title', ['options' => $bookmarks]); ?>
<?php echo $this->Form->input('systemregistration', ['options' => $bookmarks]); ?>
<?php echo $this->Form->input('systemroles', ['options' => $bookmarks]); ?>
<?php echo $this->Form->input('country', ['options' => $bookmarks]); ?>
</div>
I would like to populate each of the fields with data specific to the user logged in. Could you please help!
You don't need to do anything. If a login is successful you can access the logged in user details through the Auth component using $this->Auth->user();
If you need to add any more information to the session you can use the Session component like $this->Session->write('User.AscociatedData', $AscociatedData);
Easiest way to access this data in the view is to set authenticated user as a view variable in the controller:
$this->set('user',$this->Auth->user());
then you can accesses the users info in the view with $user e.g$user->fieldName
Not entirely sure what your asking but I hope one of my answers is relevant.
we only need to show bookmarks for the currently logged in user.
We can do that by updating the call to paginate().Make your index() action from Controller/BookmarksController.php look like:
public function index()
{
$this->paginate = [
'conditions' => [
'Bookmarks.user_id' => $this->Auth->user('id'),
]
];
$this->set('bookmarks', $this->paginate($this->Bookmarks));
$this->set('_serialize', ['bookmarks']);
}
We should also update the tags() action and the related finder method as we done for bookmarks above
Please read the tutorial
http://book.cakephp.org/3.0/en/tutorials-and-examples/bookmarks/part-two.html#fixing-list-view-and-forms

CakePhp $hasAndBelongsToMany not saving multiple select items as expected

I have the following code setup (snipped for brevity)
class BasePackage extends AppModel {
public $name = 'BasePackage';
public $hasAndBelongsToMany = array('ProductSubtype', 'ProductType');
}
class ProductType extends AppModel {
public $name = 'ProductType';
}
class ProductSubtype extends AppModel {
public $name = 'ProductSubtype';
}
Above are the simple Model classes.
/* tables in database */
base_packages
product_types
product_subtypes
base_packages_product_types
base_packages_product_subtypes
The first table is the main package that users are creating with the form, the product_* tables are pre-loaded with appropriate types and subtypes (they don't change very often), the last two are the Join tables that CakePhp wants to have
/* in BasePackage/add.ctp */
// ...
<ul class="nwblock">
<li>
<?php
echo $this->Form->input('ProductType.product_type_id', array(
'label' => 'Choose Product Type',
'type' => 'select',
'class' => 'form-control',
'style' => 'width:300px; margin-bottom:20px;',
'options' => $protypes
));
?>
</li>
</ul>
<ul class="nwblock">
<li>
<?php
echo $this->Form->input('ProductSubtype.product_subtype_id', array(
'label' => 'Choose Subtype(s)',
'multiple' => 'multiple',
'type' => 'select',
'class' => 'form-control',
'style' => 'width:300px;height:390px;margin-bottom:20px;',
'options' => $subtypes
));
?>
</li>
</ul>
// ...
Above we see the two controls that are loaded from the product_* tables. The types are a single select dropdown and the subtypes are a multiple select list.
/* in BasePackageController.php */
public function add() {
$protypes = $this->BasePackage->ProductType->find('list',
array('fields' => array('ProductType.id', 'ProductType.display')));
$subtypes = $this->BasePackage->ProductSubtype->find('list',
array('fields' => array('ProductSubtype.id', 'ProductSubtype.display')));
$this->set('protypes', $protypes);
$this->set('subtypes', $subtypes);
if ($this->request->is('post')) {
$this->BasePackage->create();
if (!empty($this->request->data)) {
$this->BasePackage->saveAll($this->request->data, array('deep' => true));
}
}
}
The process is as follows, while the user creates a new BasePackage, they select a ProductType from a dropdown box and one to many ProductSubtypes from a multiple select list. When the $this->BasePackage->saveAll() call is made, the data to be inserted into base_packages and base_packages_product_types tables is inserted correctly. However, the base_packages_product_subtypes table remains untouched.
UPDATE:
If I remove the 'multiple' => 'multiple', from the form->input options, the code saves both the producttype and the productsubtype (as expected). This is obviously not sufficient, as I need to save 1-to-many. Anyone know how to activate the 'Many' part of the HABTM?
To me BasePackage <> ProductType looks more like it should be a many-to-one relation, ie BasePackage belongsTo ProductType?
Anyways... please follow the conventions as described in the Cookbook:
http://book.cakephp.org/2.0/en/models/saving-your-data.html#saving-related-model-data-habtm
The form helper should be fed with the model name, ie ProductSubtype, and the view var should be camel backed plural, ie productSubtypes, that way CakePHP will do the rest for you automatically.
public function add() {
// ...
$this->set('productSubtypes', $subtypes);
// ...
}
echo $this->Form->input('ProductSubtype', array(
'label' => 'Choose Subtype(s)',
'class' => 'form-control',
'style' => 'width:300px;height:390px;margin-bottom:20px;'
));
Can you try with BasePackage->saveAssociated ?
http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveassociated-array-data-null-array-options-array

CakePHP Controller Testing with the Security Component

Consider this code:
Controller Code
<?php
App::uses('AppController', 'Controller');
class UsersController extends AppController {
public $components = array(
'Security',
'Session'
);
public function example() {
if ($this->request->is('post')) {
$this->set('some_var', true);
}
}
}
View Code
<?php
echo $this->Form->create();
echo $this->Form->input('name');
echo $this->Form->end('Submit');
Since I have the Security component in place, tampering with the form in any way (such as adding a field to it) will cause the request to be black-holed. I'd like to test this:
Test Code
<?php
class UsersControllerTest extends ControllerTestCase {
public function testExamplePostValidData() {
$this->Controller = $this->generate('Users', array(
'components' => array(
'Security'
)
));
$data = array(
'User' => array(
'name' => 'John Doe'
)
);
$this->testAction('/users/example', array('data' => $data, 'method' => 'post'));
$this->assertTrue($this->vars['some_var']);
}
public function testExamplePostInvalidData() {
$this->Controller = $this->generate('Users', array(
'components' => array(
'Security'
)
));
$data = array(
'User' => array(
'name' => 'John Doe',
'some_field' => 'The existence of this should cause the request to be black-holed.'
)
);
$this->testAction('/users/example', array('data' => $data, 'method' => 'post'));
$this->assertTrue($this->vars['some_var']);
}
}
The second test testExamplePostInvalidData should fail because of some_field being in the $data array, but it passes! What am I doing wrong?
By adding the 'some_field' in the data of ->testAction, the security component will assume that field is part of your app (since it's coming from your code, not a POST array) so it won't be seen as a "hack attempt".
Checking for blackholes is a little more convoluted. But Cake core tests already test the blackhole functionality, so if those tests pass, you don't need to check it in your app.
If you insist though, check out the core Cake tests for guidance:
Specifically:
/**
* test that validatePost fails if any of its required fields are missing.
*
* #return void
*/
public function testValidatePostFormHacking() {
$this->Controller->Security->startup($this->Controller);
$key = $this->Controller->params['_Token']['key'];
$unlocked = '';
$this->Controller->request->data = array(
'Model' => array('username' => 'nate', 'password' => 'foo', 'valid' => '0'),
'_Token' => compact('key', 'unlocked')
);
$result = $this->Controller->Security->validatePost($this->Controller);
$this->assertFalse($result, 'validatePost passed when fields were missing. %s');
}
Lots more examples in the file:
https://github.com/cakephp/cakephp/blob/master/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php

Lithium: how do I display related data in forms and then save them?

I'm using Lithium with MySQL.
I have a Users model that hasOne Contacts.
The Contacts model belongsTo Users.
I've listed a very basic version of my code, below.
My questions:
When I edit a user and submit the form, how do I make Users::edit save the Contact data, as well?
Also, how do I display contacts.email in the Users edit view?
models/Users.php
<?php
namespace app\models;
class Users extends \lithium\data\Model {
public $hasOne = array('Contacts');
protected $_schema = array(
'id' => array('type' => 'integer',
'key' => 'primary'),
'name' => array('type' => 'varchar')
);
}
?>
models/Contacts.php
<?php
namespace app\models;
class Contacts extends \lithium\data\Model {
public $belongsTo = array('Users');
protected $_meta = array(
'key' => 'user_id',
);
protected $_schema = array(
'user_id' => array('type' => 'integer',
'key' => 'primary'),
'email' => array('type' => 'string')
);
}
?>
controllers/UsersController.php
<?php
namespace app\controllers;
use app\models\Users;
class UsersController extends \lithium\action\Controller {
public function edit() {
$user = Users::find('first', array(
'conditions' => array('id' => $this->request->id),
'with' => array('Contacts')
)
);
if (!empty($this->request->data)) {
if ($user->save($this->request->data)) {
//flash success message goes here
return $this->redirect(array('Users::view', 'args' => array($user->id)));
} else {
//flash failure message goes here
}
}
return compact('user');
}
}
?>
views/users/edit.html.php
<?php $this->title('Editing User'); ?>
<h2>Editing User</h2>
<?= $this->form->create($user); ?>
<?= $this->form->field('name'); ?>
<?= $this->form->field('email', array('type' => 'email')); ?>
<?= $this->form->end(); ?>
Not many people know this but with lithium you can bind a form to multiple objects.
In your controller, return both a user and a contact object. Then in your form:
<?= $this->form->create(compact('user', 'contact')); ?>
You then render a field form a specific object like this:
<?= $this->form->field('user.name'); ?>
<?= $this->form->field('contact.email'); ?>
When the user submits the form, the data for both objects will be stored as:
$this->request->data['user'];
$this->request->data['contact'];
You can use this information to update the database as you would normally. If you only want to save the information if data from both objects are valid, you can call validate like this:
$user = Users::create($this->request->data['user']);
if($user->validates()) {
$userValid = true;
}
$contact = Contacts::create($this->request->data['contact']);
if($contact->validates()) {
$contactValid = true;
}
if($userValid && $userValid){
// save both objects
}
Hope that helps :)

Resources