If I want to access the public method, I can do that easily. But if I want to access the property within method, what should I do, and is it recommended??
Can I do something like this in php?
class Auth {
public function check($user = false){
$project = false; //make it somehow public
if($user == 'user1'){
$this->project = 1;
}
}
}
and than in some other place
$auth = new Auth();
$auth->check('user1')->project;
Just so you people know its possible here is the Zend framework code from
Zend-Authentication
if ($result->isValid()) {
$this->getStorage()->write($result->getIdentity());
}
I believe your question is basically regarding Fluent Interfaces or Method Chaining in conjunction with the magic method __get
Attempting to run this:
<?php
class Auth {
public function check($user = false){
$project = false; //make it somehow public
if($user == 'user1'){
$this->project = 1;
}
}
}
$auth = new Auth();
$auth->check('user1')->project;
Results in:
Notice: Trying to get property of non-object in /in/Hi5Rc on line 13
because $auth->check('user1') returns NULL (or void) and NULL doesn't have a project property.
The first thing we require is for $auth->check('user1') to return something useful. Given that $project is a boolean and $this->project is an integer, it makes the most sense to just return $project and get the value.
<?php
class Auth {
public function check($user = false){
$project = false; //make it somehow public
if($user == 'user1'){
$this->project = 1;
}
return $project;
}
}
$auth = new Auth();
print_r($auth->check('user1'));
which results in :
bool(false)
But that doesn't address your question about how to fluently access a nonpublic field or parameter.
It appears that you are operating under the misconception that these projects are taking method scoped variables like $project in your check() class and making them accessible. They are not.
Not even in your example of the Zend-Authentication.
The field $storage itself is protected, but it has public (fluent) getters/setters.
So, $this->getStorage() returns an instance of new Storage\Session() which has a public write().
Thus $this->getStorage()->write() works.
So lets take your example class and modify it a bit to demonstrate.
<?php
class Project{
/**
* #var string
*/
private $name;
/**
* #var bool
*/
private $active;
/**
* #var string
*/
private $description;
public function __construct($name = 'Default', $active = false, $description = '')
{
$this->name = $name;
$this->active = $active;
$this->description = $description;
}
/**
* #param string $name
*
* #return Project
*/
public function setName(string $name): Project
{
$this->name = $name;
return $this;
}
/**
* #param bool $active
*
* #return Project
*/
public function setActive(bool $active): Project
{
$this->active = $active;
return $this;
}
/**
* #param string $description
*
* #return Project
*/
public function setDescription(string $description): Project
{
$this->description = $description;
return $this;
}
/**
* #return string
*/
public function getName(): string
{
return $this->name;
}
/**
* #return bool
*/
public function isActive(): bool
{
return $this->active;
}
/**
* #return string
*/
public function getDescription(): string
{
return $this->description;
}
public function toArray(){
return [
'name' => $this->name,
'active' => $this->active,
'description' => $this->description
];
}
public function toJson(){
return json_encode($this->toArray());
}
public function __toString()
{
return $this->toJson();
}
}
class Auth {
/**
* #var Project
*/
private $project;
public function __construct($project = Null)
{
$this->project = is_null($project)? new Project() : $project;
}
public function check($user = false){
if($user == 'user1'){
$this->project->setName("Project: $user")->setActive(true)->setDescription("This project belongs to $user");
}
return $this;
}
/**
* #param Project $project
*
* #return Auth
*/
public function setProject(Project $project): Auth
{
$this->project = $project;
return $this;
}
/**
* #return Project
*/
public function getProject(): Project
{
return $this->project;
}
}
$auth = new Auth();
echo $auth->check('user1')->getProject();
now results in:
{"name":"Project: user1","active":true,"description":"This project
belongs to user1"}
However, you wanted to access the private field as if it were a public field without using a defined getter/setter. So lets make some more changes to the Auth class.
class Auth {
/**
* #var Project[]
*/
private $private_project;
public function __construct($project = Null)
{
$this->private_project = is_null($project)? new Project() : $project;
}
public function check($user = false){
if($user == 'user1'){
$this->private_project->setName("Project: $user")->setActive(true)->setDescription("This project belongs to $user");
}
return $this;
}
public function __get($name)
{
if ($name === 'project'){
return $this->private_project;
}
}
}
Now you can fluently access the field as you requested:
$auth = new Auth();
echo $auth->check('baduser')->project;
echo "\n";
echo $auth->check('user1')->project;
results in:
{"name":"Default","active":false,"description":""}
{"name":"Project: user1","active":true,"description":"This project belongs to user1"}
Laravel's Eloquent models make great use of the __get()function for accessing model fields dynamically. Laravel also makes great use of the __call() magic method for fluency.
I hope that helps bring some clarity.
class Auth
{
protected $project;
public function __constructor($project = false)
{
$this->project = $project;
}
public function check($user = false)
{
if($user == 'user1')
{
$this->project = 1;
}
return $this;
}
public function project()
{
return $this->project;
}
}
then you can do the following:
$auth = new Auth();
$auth->check('user1')->project(); // returns 1
or if you want you can also set another default value for the $projectin the constructor
$auth = new Auth($other_default_value);
$auth->check('user2')->project(); // returns $other_default_value
If you don't want to create extra class properties and "preserve method chaining", what about yield?
class Auth
{
public function check($user = false)
{
$project = false; // make it somehow public
if($user === 'user1'){
(yield 'project' => $project); // making it public
}
return $this;
}
}
Later on you can discover it as follows:
$array = iterator_to_array($auth->check($user));
// array(1) { ["project"] => bool(false) }
But for this to use you won't be able to use method chaining, bec. you need to retrieve generator anyway, so better to revise approach for discovering the $project.
<?php
class Auth
{
public $project;
public function check($user = false)
{
$this->project = false;//make it somehow public
if ($user == 'user1') {
$this->project = 1;
}
return $this;
}
}
$auth = new Auth();
var_dump($auth->check('user1')->project);
This will return you 1. The local variables defined in function are only accessbile inside the function not outside hence you need to define them globally
$project is a local variable in your case, visible within the scope of the check method. You could define it as a member:
class Auth {
public $project = false;
public function check($user = false){
if($user == 'user1'){
$this-project = 1;
}
}
}
However, it is recommendable to make the member public and reach it via a getter, which will check whether it was initialized and if not, initialize it:
class Auth {
private $project = false;
public getProject($user = false) {
if ($this->project === false) {
check($user);
}
return $this->project;
}
public function check($user = false){
if($user == 'user1'){
$this-project = 1;
}
}
}
You will need to add it as a class variable:
class Auth {
public $project = false;
public function check($user = false) {
if($user == 'user1'){
$this->project = 1;
}
}
}
The property is then available as follows:
$auth = new Auth ();
$auth->check ('user1');
echo $auth->project; // 1
Related
I am trying to implement a simple menu composite pattern.
These are the following classes i came up with.
MenuItem:
namespace MYNAME\MYBUNDLE\Entity;
use MYNAME\MYBUNDLE\Menu\MenuComponent;
class MenuItem implements MenuComponent
{
private $id;
private $name;
private $path;
private $parent;
private $visible;
private $createdOn;
private $templating;
private $attr;
private $children;
private $website;
private $position = 1;
public function __construct($name = null, $path = null, $attr = array(), $visible = true)
{
$this->name = $name;
$this->path = $path;
$this->visible = $visible;
$this->attr = $attr;
$this->createdOn = new \DateTime;
}
public function prePersist()
{
$this->createdOn = new \DateTime;
}
public function build()
{
$data['menu_item'] = $this;
$data['options'] = $this->attr;
if($this->hasChildren())
return $this->templating->render('MYBUNDLE:Menu:menu_dropdown.html.twig', $data);
if($this->isChild())
return $this->parent->getTemplating()->render('MYBUNDLE:Menu:menu_item.html.twig', $data);
return $this->templating->render('MYBUNDLE:Menu:menu_item.html.twig', $data);
}
public function __toString()
{
return $this->name;
}
public function setTemplating($templating)
{
$this->templating = $templating;
}
/**
* #return bool
*/
public function isChild()
{
return $this->hasParent();
}
/**
* #return bool
*/
public function hasParent()
{
return isset($this->parent);
}
/**
* #return bool
*/
public function hasChildren()
{
return count($this->children) > 0;
}
}
If left out the getters and setters to make it a bit shorter here.
As you can see this is the entity and it contains a build() function, however this function uses the render method which in my opinion shouldn't be in an entity.
MenuController
<?php
namespace MYNAME\MYBUNDLE\Controller;
use MYNAME\MYBUNDLE\Menu\Menu;
use MYNAME\MYBUNDLE\Entity\MenuItem;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class MenuController extends Controller
{
public function generateAction()
{
$menu = new Menu($this->get('templating'));
// load menu items
$items = $this->getDoctrine()->getRepository('MYBUNDLE:MenuItem')->findOrdered();
foreach($items as $item)
{
if(!$item->hasParent())
$menu->add($item);
}
return new Response($menu->build());
}
}
The MenuController gets called to render the menu:
{{ render(controller('MYBUNDLE:Menu:generate')) }}
I would also like this to be different since it doesn't look right. Perhaps it's better to create a twig function to render the menu?
MenuComponent:
namespace MYNAME\MYBUNDLE\Menu;
interface MenuComponent {
public function build();
}
Menu:
namespace MYNAME\MYBUNDLE\Menu;
class Menu implements MenuComponent
{
private $children;
private $templating;
public function __construct($templating)
{
$this->templating = $templating;
}
public function add(MenuComponent $component)
{
$component->setTemplating($this->templating);
$this->children[] = $component;
}
public function build()
{
return $this->templating->render('MYBUNDLE:Menu:menu.html.twig', array("menu_items" => $this->children));
}
}
Menu Contains the MenuComponents and will render the menu first, in each MenuItem it's build() method is called.
I think it's better to remove the rendering logic from my MenuItem entity and place this somewhere else, however i can't figure out on how to do this properly within this design pattern.
Any help or suggestion is appreciated.
I am having a model and would need to update the record. every time $count ($count = $post->save()) is being NULL. how is it possible to know whether this record saved or not. if saved, i want to display the following message 'Post updated' and if not the other message 'Post cannot update'.
This is always going to the else port. how can i know model updated correctly or not?
$post = new Application_Model_Post($form->getValues());
$post->setId($id);
$count = $post->save();
//var_dump($count); exit;
if ($count > 0) {
$this->_helper->flashMessenger->addMessage('Post updated');
} else {
$this->_helper->flashMessenger->addMessage('Post cannot update');
}
Application_Model_Post code is as below,
class Application_Model_Post
{
/**
* #var int
*/
protected $_id;
/**
* #var string
*/
protected $_title;
/**
* #var string
*/
protected $_body;
/**
* #var string
*/
protected $_created;
/**
* #var string
*/
protected $_updated;
/**
* #var Application_Model_PostMapper
*/
protected $_mapper;
/**
* Class Constructor.
*
* #param array $options
* #return void
*/
public function __construct(array $options = null)
{
if (is_array($options)) {
$this->setOptions($options);
}
}
public function setOptions(array $options)
{
$methods = get_class_methods($this);
foreach ($options as $key=> $value) {
$method = 'set'.ucfirst($key);
if (in_array($method, $methods)) {
$this->$method($value);
}
}
return $this;
}
public function setId($id)
{
$this->_id = $id;
return $this;
}
public function getId()
{
return $this->_id;
}
public function setTitle($title)
{
$this->_title = (string) $title;
return $this;
}
public function getTitle()
{
return $this->_title;
}
public function setBody($body)
{
$this->_body = $body;
return $this;
}
public function getBody()
{
return $this->_body;
}
public function setCreated($ts)
{
$this->_created = $ts;
return $this;
}
public function getCreated()
{
return $this->_created;
}
/**
* Set data mapper.
*
* #param mixed $mapper
* #return Application_Model_Post
*/
public function setMapper($mapper)
{
$this->_mapper = $mapper;
return $this;
}
/**
* Get data mapper.
*
* Lazy loads Application_Model_PostMapper instance if no mapper
* registered.
*
* #return Application_Model_PostMapper
*/
public function getMapper()
{
if (null === $this->_mapper) {
$this->setMapper(new Application_Model_PostMapper());
}
return $this->_mapper;
}
/**
* Save the current post.
*
* #return void
*/
public function save()
{
$this->getMapper()->save($this);
}
public function getPost($id)
{
return $this->getMapper()->getPost($id);
}
/**
* Update the current post.
*
* #return void
*/
public function update($data, $where)
{
$this->getMapper()->update($data, $where);
}
/**
* Find a post.
*
* Resets entry state if matching id found.
*
* #param int $id
* #return Application_Model_Post
*/
public function find($id)
{
$this->getMapper()->find($id, $this);
return $this;
}
/**
* Fetch all posts.
*
* #return array
*/
public function fetchAll()
{
return $this->getMapper()->fetchAll();
}
}
getMapper refers to the class Application_Model_PostMapper.
class Application_Model_PostMapper
{
public function save(Application_Model_Post $post)
{
$data = array(
'title'=>$post->getTitle(),
'body'=>$post->getBody(),
'created'=>$post->getCreated()
);
if (null === ($id = $post->getId())) {
unset($data['id']);
$data['created'] = date('Y-m-d H:i:s');
$post->setId($this->getDbTable()->insert($data));
} else {
$this->getDbTable()->update($data, array('id = ?'=>$id));
}
}
public function getDbTable()
{
if (null === $this->_dbTable) {
$this->setDbTable('Application_Model_DbTable_Post');
}
return $this->_dbTable;
}
}
Class of Application_Model_DbTable_Post
class Application_Model_DbTable_Post extends Zend_Db_Table_Abstract
{
protected $_name = 'posts';
}
Let me know if anything is incorrect. i am a newbie to zend and did thsi while referring the zend site. http://framework.zend.com/manual/1.12/en/learning.quickstart.create-model.html
you can extend your script like this. zend dbtable triggers the Zend_Db_Exception on any error during any insert or update.
class Application_Model_PostMapper
{
public function save(Application_Model_Post $post)
{
$data = array(
'title'=>$post->getTitle(),
'body'=>$post->getBody(),
'created'=>$post->getCreated()
);
try {
if (null === ($id = $post->getId())) {
unset($data['id']);
$data['created'] = date('Y-m-d H:i:s');
$post->setId($this->getDbTable()->insert($data));
} else {
$this->getDbTable()->update($data, array('id = ?'=>$id));
}
} catch (Zend_Db_Exception $e) {
// error thrown by dbtable class
return $e->getMessage();
}
// no error
return true;
}
}
now you can check like this
$post = new Application_Model_Post($form->getValues());
$post->setId($id);
$isSaved = $post->save();
if ($isSaved === true) {
$this->_helper->flashMessenger->addMessage('Post updated');
} else {
// error
// $isSaved holds the error message
$this->_helper->flashMessenger->addMessage('Post cannot update');
}
I currently have a function where I'm trying to refer to the $id in the class but it doesn't work:
public function getCourseInfo($cid = $this->id, $all = false)
{
}
This is my class:
class Course
{
protected $course;
protected $id;
public function __construct($cid)
{
$id = $cid;
$this->course = $this->getCourseInfo($this->id);
}
public function getCourseInfo($cid = $this->id, $all = false)
{
}
}
You haven't established $this->id yet. :)
wrong
$id = $cid;
right
$this->id = $cid;
You're also missing a closing curly-brace on your class.
Everyone in this thread is giving correct answers but no one gave a full code sample hence I'll post my suggestion:
class Course
{
/**
* #var int
*/
protected $_courseId;
/**
* #var array
*/
protected $_course;
/**
* Class constructor
*
* #param int $courseId Course ID
* #return Course
*/
public function __construct($courseId)
{
$this->_courseId = (int) $courseId;
}
/**
* Get course information
*
* #param bool $all ...
* #return array
*/
public function getCourseInfo($all = false)
{
if ($this->_course === null) {
// use $this->_courseId as needed
$this->_course = ... // populate course info
}
return $this->_course;
}
}
As you'll notice I've omitted the course id parameter from getCourseInfo() simply because it's not needed if you instantiate the class with a course id.
Secondly, I don't think you should call getCourseInfo in the constructor because the information will only be needed at a later point. Also, I added "caching" to the function so that you don't fetch data twice.
Obviously there's a high chance that I could be wrong having not seen your code but I feel this is a better structure of the code.
You need to set the $id first in your constructor.
class Course
{
protected $course;
protected $id;
}
public function __construct($cid)
{
$this->id = $cid;
$this->course = $this->getCourseInfo($id);
}
Try this
public function getCourseInfo($cid = 'default', $all = false)
{
$cid = $cid == 'default' ? $this->id : $cid;
}
Or you need completey change your class
class Course
{
protected $course;
protected $id;
public function __construct($cid)
{
$this->id = $cid;
$this->course = $this->getCourseInfo();
}
public function getCourseInfo($course_id = 0, $all = false)
{
$course_id = !$course_id ? $this->id : $course_id;
//do Somthing with
//return var;
}
No, this isn't possible, as stated on the Function arguments manual page:
The default value must be a constant
expression, not (for example) a
variable, a class member or a function
call.
Instead you could either simply pass in null as the default and update this within your function...
class Course
{
protected $course;
protected $id;
public function __construct($cid)
{
$this->id = $cid;
$this->course = $this->getCourseInfo($this->id);
}
function getCourseInfo($cid = null, $all = false) {
$cid = isset($cid) ? $cid : $this->id;
....
}
}
i want to store Student Object to array. and i try to do with below code. but it always show array count as 0
class Student
{
$StudID = 0;
$Name = null;
}
class Students
{
static private $StudentData = array();
static public function AddNewStudent($id,$name)
{
echo("AuctionID :".$AuctionID."<br/>");
try{
$objstd = new Student();
$objstd->StuID = $id;
$objstd->Name = &name;
array_push($StudentData, $objstd);
}
catch (Exception $e)
{
echo("Error".$e->getMessage());
}
}
static public function TotalStudent()
{
return count($StudentData);
}
}
Students::AddNewStudent(1,"name");
Students::AddNewStudent(2,"name2");
Students::AddNewStudent(3,"name3");
echo('Total auction running : '.Students::TotalStudent().'<br/>');
when i try to show array count it shows 0. i want to store all student data in static list
or then after when ever i want to see the list i get the list from static class only...
Because you're creating a new array instead of referencing the one you declared. Use the self keyword to reference your static object property:
class Students
{
static private $StudentData = array();
static public function AddNewStudent($id,$name)
{
echo("AuctionID :".$AuctionID."<br/>");
try{
$objstd = new Student();
$objstd->StuID = $id;
$objstd->Name = &name;
array_push(self::$StudentData, $objstd);
}
catch (Exception $e)
{
echo("Error".$e->getMessage());
}
}
static public function TotalStudent()
{
return count(self::$StudentData);
}
}
In php you have to prefix static variables with self::, like this:
array_push(self::$StudentData, $objstd);
// and in count:
return count(self::$StudentData);
Why that complicated? Your Student class should take care of it's own, same for Students. Example:
$students = new Students();
$students[] = new Student(1, "name");
$students[] = new Student(2, "name2");
$students[] = new Student(3, "name3");
printf('Total auction running : %d.', count($students));
Example output:
Total auction running : 3.
The classes:
class Student
{
/**
* #var int
*/
private $id;
/**
* #var string
*/
private $name;
/**
* #param int $id
* #param string $name
*/
public function __construct($id, $name) {
$this->id = $id;
$this->name = $name;
}
/**
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #return int
*/
public function getId()
{
return $this->id;
}
}
class Students extends ArrayObject
{
public function __construct()
{
parent::__construct(array());
}
public function offsetSet($index, $newval) {
if (!($newval instanceof Student)) {
throw new InvalidArgumentException('You can only add values of type Student.');
}
parent::offsetSet($index, $newval);
}
}
I have a structure representing a form and I want to iterate it using RecursiveIterator.
The problem is this only returns the top-level questions. What am I doing wrong?
Whole form:
class Form implements RecursiveIterator{
private $id;
private $caption;
private $other_text;
private $questions = array();
private $current;
private function __construct(DibiRow $row){
$this->id = $row->id;
$this->caption = $row->caption;
$this->other_text = $row->other_text;
$this->loadQuestions();
}
private function loadQuestions(){
$questions = dibi::query('SELECT * FROM cyp_questions WHERE form_id = %i AND parent_id IS NULL', $this->id);
while($question = $questions->fetch()) $this->questions[] = new Question($question->question_id, $question->type, $question->caption, $question->other_text, $question->triggers_unique == 1);
}
/**
* #throws InvalidArgumentException
* #param $id
* #return Form
*/
public static function loadById($id){
$form = dibi::query('SELECT * FROM cyp_forms WHERE id = %i', $id)->fetch();
if($form === false) throw new InvalidArgumentException('Form with id '.$id.' was not found.');
return new Form($form);
}
/**
* #throws FormFieldException
* #return bool
*/
public function validate($postfields){
}
public function getQuestions(){
return $this->questions;
}
public function getChildren(){
return $this->questions[$this->current];
}
public function hasChildren(){
return count($this->questions) > 0;
}
public function current(){
return $this->questions[$this->current];
}
public function key(){
return $this->current;
}
public function next(){
$this->current++;
}
public function rewind(){
$this->current = 0;
}
public function valid(){
return isset($this->questions[$this->current]);
}
}
Question:
class Question implements RecursiveIterator{
private $id;
private $type;
private $answers = array();
private $subquestions = array();
private $other_text;
private $triggers_unique;
private $caption;
private $current = 0;
public function __construct($id, $type, $caption, $other_text = null, $triggers_unique = false){
$this->id = $id;
$this->type = $type;
$this->caption = $caption;
$this->other_text = $other_text;
$this->triggers_unique = $triggers_unique;
$this->setSubQuestions();
}
private function setSubQuestions(){
$questions = dibi::query('SELECT * FROM cyp_questions WHERE parent_id = %i', $this->id);
while($question = $questions->fetch()) $this->subquestions[] = new Question($question->question_id, $question->type, $question->caption, $question->other_text, $question->triggers_unique == 1);
}
public function getOtherText(){
return $this->other_text;
}
public function getCaption(){
return $this->caption;
}
public function addAnswer($answer){
$this->answers[] = $answer;
}
public function getChildren(){
return $this->subquestions[$this->current];
}
public function hasChildren(){
return count($this->subquestions) > 0;
}
public function current(){
return $this->subquestions[$this->current];
}
public function key(){
return $this->id;
}
public function next(){
++$this->current;
}
public function rewind(){
$this->current = 0;
}
public function valid(){
return isset($this->subquestions[$this->current]);
}
public function getAnswers(){
return $this->answers;
}
}
Iteration:
$form = Form::loadById(1);
foreach($form as $question){
echo $question->getCaption().'<br />';
}
To iterate over a RecursiveIterator, you have to wrap it into a RecursiveIteratorIterator.
See some examples at
Introduction to Spl
SplWiki
The default iteration mode is only to list leaves. If you also want the containing nodes to appear in the iteration, pass RecursiveIteratorIterator::SELF_FIRST as the second argument to the constructor of the RecursiveIteratorIterator
Well, as you can see here
public RecursiveIterator RecursiveIterator::getChildren ( void )
Returns an iterator for the current iterator entry.
the method should return an object implementing the iterator. Your method return a simple array.
My guess would be to return something like:
public function getChildren(){
return new Question($this->subquestions);
}
This is because you're using a RECURSIVE iterator so it's expected to have each node of the tree of the same type (an iterator)