Asked  1 Year ago    Answers:  5   Viewed   11 times

I have 3 tables that contain user information, one for students, one for teachers and one for administrators.

They are not related in any way. I wan't to create a dashboard for the Administrators, where a list of students and teachers shows up.

The only way I found to achieve this was using the $uses variable in the Administrators controller. However, I have read in many places that this is bad practice.

Any solutions?

 Answers

2

Another, perhaps better practice is the use of ClassRegistry::init('MyModel')->myMethod() (more reading @ Cake API)

This only loads the object when it's used, as opposed to loadModel or uses, with ClassRegistry the models are treated as singletons.

--

that you are doing something wrong: you need access to a model that has nothing to do with your current controller.

There are plenty of conditions where you would need to access all of your models data, from one controller, but never a definitive answer on how to do it without breaking convention!

Thursday, April 1, 2021
 
4

CakePHP does not offer what you describe at the Model level out of the box. That is to say there is no Model property of defaultFields that is used on every find()

As you noted, you could specify this at the association level by setting the fields property. However, this would only work when you were retrieving the Model across one of these relationships.

In the end, you're going to be setting this in your find(). You could minimize repeating yourself by adding a property to your model like so:

var $defaultFields = array('Model.field1', 'Model.field2', ...);

Then in your find():

$this->Model->find('fields' => $this->Model->defaultFields, ...);

This has obvious limitations, but at least provides some encapsulation and therefore flexibility.

Note: A more invasive approach could use beforeFind();. In which case you would not need to adjust every find(). But your mileage may vary based on your usage.

Thursday, April 1, 2021
 
sohum
 
1

Try adding the id field of the guest record also in your view:

<?php
echo $this->Form->create("Booking", array('type' => 'post'));
echo $this -> Form -> hidden('Booking.id');
echo $this -> Form -> input('Booking.property_id');
echo $this -> Form -> input('Booking.checkin');
echo $this -> Form -> input('Booking.checkout');
echo $this -> Form -> input('Guest.0.id');
echo $this -> Form -> input('Guest.0.firstname');
echo $this -> Form -> end("Submit");

?>

Thursday, April 1, 2021
 
samayo
 
5

Because cursors take up memory and create locks.

What you are really doing is attempting to force set-based technology into non-set based functionality. And, in all fairness, I should point out that cursors do have a use, but they are frowned upon because many folks who are not used to using set-based solutions use cursors instead of figuring out the set-based solution.

But, when you open a cursor, you are basically loading those rows into memory and locking them, creating potential blocks. Then, as you cycle through the cursor, you are making changes to other tables and still keeping all of the memory and locks of the cursor open.

All of which has the potential to cause performance issues for other users.

So, as a general rule, cursors are frowned upon. Especially if that's the first solution arrived at in solving a problem.

Wednesday, June 2, 2021
5

First, in general there's no problem with calling methods in a constructor. The issues are specifically with the particular cases of calling overridable methods of the constructor's class, and of passing the object's this reference to methods (including constructors) of other objects.

The reasons for avoiding overridable methods and "leaking this" can be complicated, but they basically are all concerned with preventing use of incompletely initialised objects.

Avoid calling overridable methods

The reasons for avoiding calling overridable methods in constructors are a consequence of the instance creation process defined in §12.5 of the Java Language Specification (JLS).

Among other things, the process of §12.5 ensures that when instantiating a derived class[1], the initialisation of its base class (i.e. setting its members to their initial values and execution of its constructor) occurs before its own initialisation. This is intended to allow consistent initialisation of classes, through two key principles:

  1. The initialisation of each class can focus on initialising only the members it explicitly declares itself, safe in the knowledge that all other members inherited from the base class have already been initialised.
  2. The initialisation of each class can safely use members of its base class as inputs to the initialisation of its own members, as it is guaranteed they've been properly initialised by the time the initialisation of the class occurs.

There is, however, a catch: Java allows dynamic dispatch in constructors[2]. This means that if a base class constructor executing as part of the instantiation of a derived class calls a method that exists in the derived class, it is called in the context of that derived class.

The direct consequence of all of this is that when instantiating a derived class, the base class constructor is called before the derived class is initialised. If that constructor makes a call to a method that is overridden by the derived class, it is the derived class method (not the base class method) that is called, even though the derived class has not yet been initialised. Evidently this is a problem if that method uses any members of the derived class, since they haven't been initialised yet.

Clearly, the issue is a result of the base class constructor calling methods that can be overriden by the derived class. To prevent the issue, constructors should only call methods of their own class that are final, static or private, as these methods cannot be overridden by derived classes. Constructors of final classes may call any of their methods, as (by definition) they cannot be derived from.

Example 12.5-2 of the JLS is a good demonstration of this issue:

class Super {
    Super() { printThree(); }
    void printThree() { System.out.println("three"); }
}
class Test extends Super {
    int three = (int)Math.PI;  // That is, 3
    void printThree() { System.out.println(three); }

    public static void main(String[] args) {
        Test t = new Test();
        t.printThree();
    }
}

This program prints 0 then 3. The sequence of events in this example is as follows:

  1. new Test() is called in the main() method.
  2. Since Test has no explicit constructor, the default constructor of its superclass (namely Super()) is called.
  3. The Super() constructor calls printThree(). This is dispatched to the overriden version of the method in the Test class.
  4. The printThree() method of the Test class prints the current value of the three member variable, which is the default value 0 (since the Test instance hasn't been initialised yet).
  5. The printThree() method and Super() constructor each exit, and the Test instance is initialised (at which point three is then set to 3).
  6. The main() method calls printThree() again, which this time prints the expected value of 3 (since the Test instance has now been initialised).

As described above, §12.5 states that (2) must happen before (5), to ensure that Super is initialised before Test is. However, dynamic dispatch means that the method call in (3) is run in the context of the uninitialised Test class, leading to the unexpected behaviour.

Avoid leaking this

The restriction against passing this from a constructor to another object is a little easier to explain.

Basically, an object cannot be considered fully initialised until its constructor has completed execution (since its purpose is to complete the initialisation of the object). So, if the constructor passes the object's this to another object, that other object then has a reference to the object even though it hasn't been fully initialised (since its constructor is still running). If the other object then attempts to access an uninitialised member or call a method of the original object that relies on it being fully initialised, unexpected behaviour is likely to result.

For an example of how this can result in unexpected behaviour, please refer to this article.


[1] Technically, every class in Java except Object is a derived class - I just use the terms 'derived class' and 'base class' here to outline the relationship between the particular classes in question.
[2] There's no reason given in the JLS (as far as I'm aware) as to why this is the case. The alternative - disallowing dynamic dispatch in constructors - would make the whole issue moot, which is probably exactly why C++ doesn't allow it.
Wednesday, June 16, 2021
 
Akarun
 
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :