Doctrine nestedset delete - php

I have some models witch are using Doctrine nestedset feature.
I want to add delete functionaly of elements from tree since that is required in my application. I was trying with snippets from documentation but I am getting a very strange error with that code.
YAML is here: http://pastie.org/820978
And I am trying with this code in my Menu class witch extends generated abstract class BaseMenu and BaseMenu extends Doctrine_Record :)
Anyway my code:
public function getMenuItem($id)
{
return Doctrine::getTable('Menu')->find($id);
}
public function delete($id)
{
$item = $this->getMenuItem($id);
//echo get_class($item); will return Menu so object exists !?
$item->getNode()->delete();
}
And I get this an error:
Fatal error: Call to a member function
getNode() on a non-object
And I just noticed that get_class($item) is trowing a warring (so that probabbly is reason for this strange behavior):
Warning: get_class() expects parameter
1 to be object, boolean given in...
However I need a solution for this and all hints are welcome...

getNode() returns a Doctrine_Node, not a Doctrine_Record.
A Doctrine_Record can be deleted, but a Doctrine_Node cannot be deleted -- because it is not persistent anyway.
The correct logic would simply be:
$item = $this->getMenuItem($id)->delete();
Also, don't name a method in your model 'delete'!! This will override Doctrine_Record's delete() method, which will drive you crazy trying to debug it.

I personally don't like using Doctrine::getTable("table_name") because it doesn't make the code very dry. If for some reason "table_name" ever changes, you'll have to change it in alot of places.
I used Doctrine in Zend Framework apps, so my typical pattern of use involves instantiating a protected instance of every model in my module.
Using that pattern, I would just do this in my controller
$this->_Menu
->getTable()
->find($id)
->getNode()
->delete();
If you really want to keep your functions similar, I would use something like this
public function getMenuItem($id)
{
if (empty($id))
{
throw new Exception ("A parameter of id is required to retrieve a menu item".);
}
return $this->getTable()->find($id);
}
public function delete($id)
{
$item = $this->getMenuItem($id);
if ($item instanceof Doctrine_Record == false)
{
throw new Exception("Item is not a valid Menu Record.");
}
$item->getNode()->delete();
}

Answer is in your question: $item is not object (i guess it's value is false, but you can use var_dump($item)), because there is no row with such id in DB (also I guess your $id is null)
Warning: get_class() expects parameter 1 to be object, boolean given in...
Fatal error: Call to a member function getNode() on a non-object

Related

PHPunit: How to change Mock object propriety from an external function

I am using Symfony and I'm trying to test the addStudentCard function in "Student" class, which adds a "StudentCard" object to $studentCards array collection propriety AND a "Student" object to $student propriety in "StudentCard" class. This is how I did it:
class StudentCard {
private $student;
public function getStudent();
public function setStudent();
//...
}
class Student {
private $studentCards;
public function getStudentCards();
public function addStudentCard(StudentCard $studentCard){
$studentCard->setStudent($this);
$this->studentCards[] = $studentCard;
return $this;
//...
}
What I want to achieve is to test this addStudentCard function using a MockBuilder, I have already done this without using mocks by doing:
class StudentTest extends AbstractTestCase {
public function testAddStudentCard(){
$studentCard = new StudentCard();
$student = new Student();
$student->addStudentCard($studentCard);
$student->assertSame($studentCard, $student->getStudentCards()[0]);
$student->assertSame($student, $studentCard->getStudent());
}
This works as expected with no problem.
What I would like is to replace the line:
$studentCard = new StudentCard();
with something like this:
$studentCard = $this->getMockBuilder(StudentCard::class)->getMock();
But what I get is the error:
Failed asserting that null is identical to an object of class Student.
The problem with your scenario is, that you are asserting that the mock returns the original student:
$student->assertSame($student, $studentCard->getStudent());
If the $studentCard is a Mock object, it doesn't return the original object unless you tell it to do so. But since you are already using a mock, there is no need to test that.
What you actually want to test in this case is, that the $student was assigned back to the $studentCard. That is what expectations are for.
So in your particular case you would go with:
$studentCard->expects($this->once())->method('setStudent')->with($student);
// ...
$student->addStudentCard($studentCard);
Make sure that you have the line there (as I shown in the code) before you call addStudentCard, otherwise the test will fail that the expectation has not been met.
After you set the expectations, there is no need to run any assertions (and you should not) on the mock objects.
The answer Ondrej Führer provided is the right answer to the problem I described.
I had also a removeStudentCard method that deletes the student from the studentCard object, so $this->once() was not appropriate for my case. In order to test this, I did exactly the same thing Ondrej Führer suggested with some modifications, so the code line I added was:
$studentCard->expects($this->exactly(2))->method('setStudent')->withConsecutive(
[$student],
[null]
);
//...
$student->addStudentCard($studentCard);
//...
$student->removeStudentCard($studentCard);
This is self explaining, the method setContact is expected to be called exaclty two times with $student as an argument for the first time, and null in the second call.
Hopefully this would be helpful for anyone looking to do something similar.

ZF2 - Call to a member function on a non-object error

I am trying to implement a simple roll-a-dice service and use it in a .phtml file. I know my issue has been reported often on SO, but I could not find a solution in other questions.
I get the following error message:
Fatal error: Call to a member function getDiceResult()
on a non-object in rolladice.phtml on line 11
Here is line 11:
<?php
$result = $this->rollADiceService->getDiceResult();
echo "<p>Roll-a-dice result: ".$result."</p>";
?>
The controller is set-up as following:
class RollADiceController extends AbstractActionController
{
private $rollADiceService;
public function setPluginManager(PluginManager $plugins) {
parent::setPluginManager($plugins);
$this->rollADiceService = $this->getServiceLocator()
->get('RollADiceService');
}
...
In Module.php, I have:
public function getServiceConfig()
{
return array(
'factories'=>array(
'LoginLogoutService' => function() {
return new LoginLogoutService();
},
'RollADiceService' => function() {
return new RollADiceService();
},
),
);
}
I am using the same technique (i.e., setPluginManager) to retrieve instances of my services in other controllers without issues. What am I doing wrong?
P.S.: Using a debugger, I can see that setPluginManager() is called, but that the $this->rollADiceService variable is initialized with null. Why?
Ultimately, there were two issues:
i) I refactored my code to pass the result of the call to rollADiceService->getDiceResult() as a variable which $this can access.
ii) Hijacking setPluginManager() is not a recommended practice. I've implemented factories for my controllers depending on services.
your problem is realy simple I think.
<?php
$result = $this->rollADiceService()->getDiceResult();
echo "<p>Roll-a-dice result: ".$result."</p>";
?>
Add these brackets after rollADiceService should fix your problem. Without brackets, Zend View instance try to access to view variable named rollADiceService, which is obviously NULL.
By adding brackets, you tell to View it should look for rollADiceService in registered view helpers.
I hope it helps :)

Zend Framework, Returning class methods from model

I have a Model which I would like to use all variables in a drop down lis on a form.
protected $_scheduledTime;
protected $_isLive;
protected $_isQueued;
protected $_url;
I was hoping, something like this would work, but it doesn't
public function getMethods() {
$methods = getclass_methods($this);
return $methods;
}
Calling this returns this fatal error:
Fatal error: Call to undefined function getclass_methods()
What I want to achieve is an automatically updated form options when I update the model (which, could be quite frequently)
I might have answered my own question, in that I would build an array in the model that returns upon a call... but if there is already a method that can do this, then it would be much appreciated.
Thank you in advance.
get_declared_classes() // gives you all declared classes
get_class_methods() // gives you all methods of class
get_class_vars() // gives you properties of the class
get_object_vars() // gives you propertis of an object
get_parent_class() // gives you parent class of the current class
The function is get_class_methods, not getclass_methods (missing an '_')

Codeigniter model error

I created a new model class as my_model.php inside /models folder, and a function inside it to load all elements:
function get_all(){
$query = $this->db->get('test'); //test is my table
return $query->result();
}
In the controller, I instantiated the class and called the method;
$this->load->model('my_model');
$res = $this->my_model->get_all();
But, this throws me error saying:
Fatal error: Call to a member function get() on a non-object in /var/www/testapp/application/models/my_model.php on line 7
This line 7 points to the portion of the code where I have used $this->db. I tried to see the value of $db but I think it is magic accessor __get and __set, so I could not see the value of this property before calling that method.
I tried googling for several other cases but none of them match my scenarios and rather none of them could solve my problem.
You have to load the Database first
$this->load->database();
So, all code:
function get_all(){
$this->load->database();
$query = $this->db->get('test'); //test is my table
return $query->result();
}
Or, load database in your __construct method.
Or, IMO, It's better to autoload database by changing application/config/autoload.php, example is below.
$autoload['libraries'] = array('database','form_validation'); //form_validation is for example only
In CodeIgniter, you should load database model before you use it.
Use $this->load->database(); to load database model.
Your error is actually quite simple:
return $query->result;
Should be:
return $query->result();
Sometimes the line number reported by a PHP error isn't exactly the one you think it is, the parser is just doing it's best and reporting where it found an error.
There's one more issue:
$res = $this->my_model->getAll();
Should be:
$res = $this->my_model->get_all();
You have called your own function by the wrong name.

PHP ambiguous statement when setting dynamic property value of class

I have a class called PreferenceCollection, which I load for a user into $_SESSION['Preferences'] once they log in to my web application. This simple class uses magic methods for getting and setting values. Here are the relevant parts:
class PreferenceCollection {
private $prefs;
function __construct() {
$this->prefs=Array();
}
function __get($var) {
return $this->prefs[$var];
}
function __set($var, $value) {
$this->prefs[$var]=$value;
$this->save();
}
}
Later on in my code, I have found it necessary to set the value of a dynamically chosen property. For this example, $key='pref_some_preference' and value='something'.
$_SESSION['Preferences']->substr($key, 5)=$value;
What I expect is the equivalent of $_SESSION['Preferences']->some_preference=$value. That is, the __set magic method will be called with the first parameter of some_preference, and the second parameter, $value.
Instead, this line gives me a fatal error:
PHP Fatal error: Can't use method return value in write context
I assume that PHP is interpreting that I want to set the return value of the substr() call to something, rather than setting the property.
Is my interpretation of what is happening correct? How would I get around this problem?
For now I am working around the issue with a public set method, but am curious how I could set that property directly.
I think you want
$_SESSION['Preferences']->{substr($key, 5)} = $value;
The braces {} are required to guide the PHP parser into the correct direction ;-) Given that $key is 12345test this would call __set('test', $value).

Categories