Question

I am building an app with cakePHP and I'm quite new in it. what I wanna do now is this. Let me explain in few words: I have 2 models, Item and Typologies. One item can have many typologies. So Typology table has a foreign key - item_id - which refers to item. Now I want to prevent user from deleting Items if there are still typologies referring to this Item.

My Item Model is This:

<?php
App::uses('AppModel', 'Model');
/**
 * Item Model
 *
 * @property ItemLocation $ItemLocation
 * @property ItemCharacteristic $ItemCharacteristic
 * @property FirstSeller $FirstSeller
 * @property SecondSeller $SecondSeller
 * @property User $User
 * @property Contact $Contact
 * @property ItemPicture $ItemPicture
 * @property Typology $Typology
 */
class Item extends AppModel {
public $name = 'Item';

/**
 * Primary key field
 *
 * @var string
 */
    public $primaryKey = 'id';
/**
 * Display field
 *
 * @var string
 */
    public $displayField = 'title';

/**
 * Validation rules
 *
 * @var array
 */
    public $validate = array(
        'id' => array(
            'blank' => array(
                'rule' => 'blank',
                'on' => 'create',
            ),
        ),
        'title' => array(
            'words' => array(
                'rule' => array('custom', '/[0-9A-Za-z\._-]/'),
                'message' => 'The Item name can only contain letters, numbers and spaces.',
            ),
            'maxLength' => array(
                'rule' => array('maxLength', 100),
                'message' => 'The Item name must not be longer than 100 characters.',
            ),
            'notEmpty' => array(
                'rule' => array('notEmpty'),
                'message' => 'The Item name must not be empty.',
            ),
            'isUnique' => array(
                 'rule' => 'isUnique',
                 'message' => 'This Item name already exists.',
            ),
        ),

        'user_id' => array(
            'numeric' => array(
                'rule' => array('numeric'),
                //'message' => 'Your custom message here',
            ),
            'notEmpty' => array(
                'rule' => array('notEmpty'),
                'message' => 'Not Empty',
            ),
        ),
        'created' => array(
            'datetime' => array(
                'rule' => array('datetime'),
                //'message' => 'Your custom message here',
            ),
        ),
    );

/**
 * belongsTo associations
 *
 * @var array
 */
    public $belongsTo = array(
        'ItemUser' => array(
            'className' => 'User',
            'foreignKey' => 'user_id',
            'conditions' => '',
            'fields' => '',
            'order' => ''
        )
    );

/**
 * hasMany associations
 *
 * @var array
 */
    public $hasMany = array(
        'ItemTypologies' => array(
            'className' => 'Typology',
            'foreignKey' => 'item_id',
            'dependent' => false,
            'conditions' => '',
            'fields' => '',
            'order' => '',
            'limit' => '',
            'offset' => '',
            'exclusive' => '',
            'finderQuery' => '',
            'counterQuery' => ''
        )
    );

And the Typology Model is this:

<?php
App::uses('AppModel', 'Model');
/**
 * Typology Model
 *
 * @property Item $Item
 * @property TypologyCategory $TypologyCategory
 * @property TypologyCondition $TypologyCondition
 * @property User $User
 * @property TypologyPicture $TypologyPicture
 */
class Typology extends AppModel {
public $name = 'Typology';
/**
 * Primary key field
 *
 * @var string
 */
    public $primaryKey = 'id';
/**
 * Display field
 *
 * @var string
 */
    public $displayField = 'title';

/**
 * Validation rules
 *
 * @var array
 */
    public $validate = array(
        'id' => array(
            'blank' => array(
                'rule' => 'blank',
                'on' => 'create',
            ),
        ),
        'item_id' => array(
            'numeric' => array(
                'rule' => array('numeric'),
                'message' => 'Chose Which Object This Typology Belongs To',
            ),
            'notEmpty' => array(
                'rule' => array('notEmpty'),
                'message' => 'Can Not be Empty',
            ),
        ),
        'title' => array(
            'words' => array(
                'rule' => array('custom', '/[0-9A-Za-z\._-]/'),
                'message' => 'The Typology name can only contain letters, numbers and spaces.',
            ),
            'maxLength' => array(
                'rule' => array('maxlength', 50),
                'message' => 'The Typology name must not be longer than 50 characters.',
            ),
            'notEmpty' => array(
                'rule' => array('notEmpty'),
                'message' => 'Typology Title Can not be Empty',
            ),
            'isUnique' => array(
                'rule' => 'isUnique',
                'message' => 'Typology Name Should be Unique',
            ),
        ),
        'description' => array(
            'words' => array(
                'rule' => array('custom', '/[0-9A-Za-z\._-]/'),
                'message' => 'The Typology name can only contain letters, numbers and spaces.',
            ),
            'maxLength' => array(
                'rule' => array('maxlength', 350),
                'message' => 'The Typology name must not be longer than 350 characters.',
            ),
            'notEmpty' => array(
                'rule' => array('notEmpty'),
                'message' => 'Description can not be Empty',
            ),
        ),

        'user_id' => array(
            'numeric' => array(
                'rule' => array('numeric'),
                'message' => 'Chose the user who created this typology',
            ),
            'notEmpty' => array(
                'rule' => array('notEmpty'),
                //'message' => 'Your custom message here',
            ),
        ),
        'created' => array(
            'datetime' => array(
                'rule' => array('datetime'),
                //'message' => 'Your custom message here',
            ),
        ),
    );

/**
 * belongsTo associations
 *
 * @var array
 */
    public $belongsTo = array(
        'TypologyItem' => array(
            'className' => 'Item',
            'foreignKey' => 'item_id',
            'conditions' => '',
            'fields' => '',
            'order' => ''
        ),
        'TypologyUser' => array(
            'className' => 'User',
            'foreignKey' => 'user_id',
            'conditions' => '',
            'fields' => '',
            'order' => ''
        )
    );

Now what I saw and wrote was this, and if anyone has any idea I'd really appreciate it:

// using app/Model/Item.php
// In the following example, do not let an Item to be deleted if it
// still contains Typologies Attached to it.
// A call of $this->Typology->delete($id) from TypologiesController.php has set
// $this->id .
// Assuming 'ItemTypologies hasMany Typology', we can access $this->Typoogy
// in the model.

public function beforeDelete($cascade = true) {
    $count = $this->Typology->find('count', array('conditions' => array('item_id' => $this->Typology->id)));
    if ($count == 0) {
        return true;
    } else {
        return false;
    }
} 

And when I try to delete a Item which has typologes or An item Which doesn't it shows this error!

Fatal Error

Error: Call to a member function find() on a non-object
File: C:\wamp\www\project\app\Model\Item.php
Line: 449

How can I Solve it!!

Was it helpful?

Solution

The logic is similar to what theotherdy already explained.

In your controller code the delete() method as follows:

public function delete($id = null) {
    $this->request->onlyAllow('post', 'delete');
    $options = array('conditions' => array('Item.' . $this->Field->primaryKey => $id));
    $item = $this->Item->find('first', $options);
    if (!$item) {
        throw new NotFoundException(__('Invalid item'));
    }
    if (isset($item['Typology']) && count($item['Typology'])) {
        $this->setFlash(__("Item '%s' is being used by %s Typologies.<br />Cannot delete.", $item['Item']['title'], count($item['Tipology'])));
    } else {
        $this->Item->id = $id;
        if ($this->Item->delete()) {
            $this->setFlash(__('Item deleted'));
        } else {
            $this->setFlash(__('Item was not deleted'));
        }
    }
    $this->redirect(array('action' => 'index'));
}

OTHER TIPS

I would have said that you want your DB to be preventing deletion of items which are referred to by other typologies by making sure those foreign key constraints are in the db. Then your UI should not give people the option of deleting items which are referred to by a typology, but maybe you do want to give them the option of removing the typologies from an item.

I have no idea what sort of UI you are proposing but (off the top of my head, unchecked code so apologies for any typos/errors), say you had an index action/view of items, you might have this in your ItemsController.php index action:

$items = $this->Item->find('all')
$this->set('items', $items);

then in your Views/Items/index.ctp you might have:

<?php foreach ($items as $item): ?>
    <h1><?php echo $item['Item']['name'];?>
    <?php 
    if(!isset($item['Typology'])||count($item['Typology'])==0){
        //ie this items has no Typologies 
        //Create delete button/link for this item
        }
    elseif(isset($item['Typology'])&&count($item['Typology'])>0){
        //we have some typologies for this item
        foreach ($item['Typology'] as $typology){
            //info about the typology and possible delete button/link
            }
        }
    ?>
<?php endforeach; ?>

HTH

ADDING MORE DETAIL IN THE CONTROLLER - expanding on @savedario 's answer

If your models are set up with the proper foreign key constraints then

$this->Item->delete()

will fail if there are any violations of those constraints so, as @savedario says, you can test for success with

if ($this->Item->delete())

or you can go one step further, as @savedario illustrates but which I think is done more elegantly and generically here: http://joshuapaling.com/post/catching-integrity-constraint-violations-when-deleting-records and throw an exception if there are linked records which you can then test for in the following (copied from joshuapaling's blog) :

 try {
    if ($this->MyModel->delete()) {
        $this->Session->setFlash('Record deleted', 'default', array(), 'good');
    } else {
        $this->Session->setFlash('Record was not deleted. Unknown error.', 'default', array(), 'bad');
    }
} catch (Exception $e) {
    $this->Session->setFlash("Delete failed. {$e->getMessage()}", 'default', array(), 'bad');
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top