56

Can I use another Model inside one model?

Eg.

<?php
class Form extends AppModel
{
    var $name='Form';
    var $helpers=array('Html','Ajax','Javascript','Form');
    var $components = array( 'RequestHandler','Email');

    function saveFormName($data)
    {
        $this->data['Form']['formname']=$data['Form']['formname'];
        $this->saveField('name',$this->data['Form']['formname']);
    } 

    function saveFieldname($data)
    {
        $this->data['Attribute']['fieldname']=$data['Attribute']['fieldname'];
    }

}
?>
Chuck Burgess
  • 11,600
  • 5
  • 41
  • 74
useranon
  • 29,318
  • 31
  • 98
  • 146
  • 13
    excuse me? helpers and components in a Form model that saves attributes? this is all over the place, neither mvc nor cake. – Alexander Morland Jun 11 '09 at 19:21
  • 3
    Brad takes the cake here... CakePHP, if you use it right, will automatically link the instances together, via associations.. There's really no point in instantiating another instance of the model if it's already there... If you are unable to associate the models directly, then looking at instantiating a new model might be an option. – Andrew Senner Mar 27 '13 at 14:58
  • 1
    `TableRegistry::get('Products')` for cakephp 3. See @tarikul05 answer – styks Jun 06 '17 at 03:07

8 Answers8

120

Old thread but I'm going to chime in because I believe the answers to be incomplete and lacking in "why". CakePHP has three ways to load models. Though only two methods work outside of a Controller, I'll mention all three. I'm not sure about version availability but this is core stuff so I believe they'll work.

App::import() only finds and require()s the file and you'll need to instantiate the class to use it. You can tell import() the type of class, the name and file path details.

ClassRegistry::init() loads the file, adds the instance to the object map and returns the instance. This is the better way to load something because it sets up "Cake" things as would happen if you loaded the class through normal means. You can also set an alias for the class name which I've found useful.

Controller::loadModel() uses ClassRegistry::init() as well as adds the Model as a property of the controller. It also allows $persistModel for model caching on future requests. This only works in a Controller and, if that's your situation, I'd use this method before the others.

JCotton
  • 11,650
  • 5
  • 53
  • 59
  • 2
    A similar set of statements explained by gwoo himself: http://groups.google.com/group/cake-php/msg/794c451038c0c798 – icc97 Sep 18 '11 at 10:59
  • Using `Controller::loadModel()` in a model throws a warning about the static call: `Non-static method Controller::loadModel() should not be called statically, assuming $this from incompatible context` – Matt Mar 08 '14 at 14:53
  • @Matt the method Controller::loadModel() is meant to be called within your current controller like `$this->loadModel('aModel');` – Kvothe Sep 02 '14 at 18:26
  • 2
    Beware these answers are no longer applicable to CakePHP v3+ – emersonthis Jul 12 '16 at 22:51
26

You can create instances of other models from within any model/controller using one of these two methods.

If you're using Cake 1.2:

App::import('model','Attribute');
$attr = new Attribute();
$attr->save($dataYouWantToSavetoAttribute);

If you're using Cake 1.1:

loadModel('Attribute');
$attr = new Attribute();
$attr->save($dataYouWantToSavetoAttribute);
brettkelly
  • 27,655
  • 8
  • 56
  • 72
  • 10
    App::import() is not very good for models. $attr = ClassRegistry::init('Attribute') should be used. – dr Hannibal Lecter Jun 11 '09 at 16:47
  • Actually my form has fields(Attributes) like associations – useranon Jun 12 '09 at 06:19
  • @drHannibalLecter: ClassRegistry::init() will return an instance of the model which will use more memory. Won't it be more messy? – Fr0zenFyr Aug 26 '13 at 12:22
  • @Fr0zenFyr: As far as I know, it will return an already existing instance of a model, it won't create a new one if there is one in the class registry. Besides, what else would it return if not an instance of the model? – dr Hannibal Lecter Aug 27 '13 at 12:34
  • @drHannibalLecter: App::import() `Finds classes based on $name or specific file(s) to search. Calling App::import() will not construct any classes contained in the files. It will only find and require() the file.` in API documentation(which is not good). I don't understand why API docs recommend use of ClassRegistry::init() over App::import()..!! For cakephp 2.0 and above, `App::uses()` is good. – Fr0zenFyr Aug 27 '13 at 13:04
  • @Fr0zenFyr: Because it's easier to use? You need an instance of the model anyway, so why not use the one cake gives you? Creating one on your own with `new` is pretty much a code smell in cake. If you're not talking about models, but your own custom classes/libraries, that's a different matter entirely. – dr Hannibal Lecter Aug 27 '13 at 13:20
  • Even after a couple of years, I still say there's no good or bad way of doing it. Use `App::import()` if you need "fresh and untouched" instance of a model and use `ClassRegistry::init()` if you want an existing instance of model from registry. Difference lies in the need for your case. For example, with `ClassRegistry::init()` in a behavior `afterFind()` you wont get full set of model relationships if you had used `'contains'` in your `find()` call. `App::import()` allows you to create a fresh instance to get all default information about the model in question. That's just one case. – Fr0zenFyr Sep 12 '18 at 05:12
16

An obvious solution everyone missed is to create an association between two models, if appropriate. You can use it to be able to reference one model from inside another.

class Creation extends AppModel {
    public $belongsTo = array(
        'Inventor' => array(
            'className'  => 'Inventor',
            'foreignKey'  => 'inventor_id',
        )
    );

    public function whoIsMyMaker() {
        $this->Inventor->id = $this->field('inventor_id');
        return $this->Inventor->field('name');
    }
}
Brad Koch
  • 19,267
  • 19
  • 110
  • 137
  • This is good for database wise, however in php you can't execute functions in that other model through this :( – bicycle Sep 22 '13 at 03:58
  • 1
    `$this->Inventor` should be an instance of the `Inventor` model; as long as your methods are public you should be able to invoke them just fine. If you're experiencing different behavior I suggest posting a new question. – Brad Koch Sep 22 '13 at 04:00
  • Hmmmm now i'm checking the doc i see what you're saying is indeed true. I thought this behaviour was only reserved for java/.net... Thanks for pointing out, this should actually be the best answer :) – bicycle Sep 22 '13 at 04:22
11

In CakePHP 1.2, it's better to use:

ClassRegistry::init('Attribute')->save($data);
Brad Koch
  • 19,267
  • 19
  • 110
  • 137
michas
  • 147
  • 3
4

This will do simply

<?php
   class Form extends AppModel
    {
       //...
          $another_model = ClassRegistry::init('AnotherModel');
       //...  
    }
?>
Umar Sid
  • 1,317
  • 1
  • 17
  • 24
2

In CakePHP 3 we may use TableRegistry::get(modelName)

use Cake\ORM\TableRegistry;

$itemsOb = TableRegistry::get('Items');
$items = $itemsOb->find("all");
debug($items);
tarikul05
  • 1,843
  • 1
  • 16
  • 24
  • 1
    Or if the models are associated. i.e. Products -> Categories, you can call it directly. `$this->Categories` from within the ProductsTable – styks Jun 06 '17 at 03:03
  • @styks nice, that did it! I think this suggestion deserves to be a standalone answer! :) – ᴍᴇʜᴏᴠ Dec 24 '20 at 18:59
1

If you want to use Model_B inside Model_A, add this line at the beginning of Model_A file:

App::uses('Model_B_ClassName', 'Model');

and then you will be able to use it inside Model_A. For example:

$Model_B = new Model_B_ClassName();
$result = $Model_B->findById($some_id);
Brad Koch
  • 19,267
  • 19
  • 110
  • 137
Zbigniew Ledwoń
  • 682
  • 1
  • 6
  • 19
-3
var $uses = array('ModeloneName','ModeltwoName');

By using $uses property, you can use multiple models in controller instead of using loadModel('Model Name').

App::import('model','Attribute');

is way to use one model into other model. Best way will be to used association.

Garrett Hyde
  • 5,409
  • 8
  • 49
  • 55
Urdesh Kumar
  • 1,120
  • 1
  • 17
  • 23