I'm working on a project that requires a lot of separate objects (classes) to work in unison. I've come up with a solution to my problem, but I just wanted to hear some other perspectives, because I feel like i'm missing something.
So essentially, I have a visitor and a location class, each are initialized and are stored within a variable, such as $visitor and $location; both having data unique to the visitor and location referenced on the page call.
What I want/need to do is essentially make a 'bridge' so I can call a method that will affect both of these objects. For instance, I want to be able to call $bridge->favorite(); and it will both add that favorite reference to the visitor's profile in addition to increasing the number of favorites the location has.
What I have done right now is essentially made a bridge:
$bridge = new Bridge($locationclass, $visitorclass);
$bridge->favorite();
So it essentially calls another class I have, 'bridge,' with the method favorite, then would use the two classes already set. Which I think is an okay solution, but I feel like i'm missing a better solution.
All input would be extremely helpful, thank you for your time!
If you need to do this kind of stuff, it means that your application is not well designed.
To answer your example, what you have to do is adding the location to the visitor's favorite places, only.
And if you want to count how many times were a location added to favorites, count how many visitors have it in their favorites. Don't store two times the same information.
class Bridge{
private $classes;
function construct($classes)
{
$this->classes = $classes;
}
function _call($name,$params)
{
foreach($classes as $class)
{
if(method_exists($class,$name)){
return call_user_method_array($name,$class,$params);
}
}else{
...
}
}
Related
Why use $LinkingMode in Silverstripe? How can the $LinkingMode of Silverstripe have only one value at a time when it can be a section and current at the same time or link and section as well?
Here is the answer and Silverstripe experts (wmk,3dgo,greg,munomono, etc.) here please correct me if I'm wrong. $LinkingMode is used for styling, notice in the examples. Also there can be only one state for a $LinkingMode since it is connected to a single menu and menu level (i.e. menu(1), menu(2)...etc). So if you want to style other menu levels then assign a class=$LinkingMode to that menu in that level. The Silverstripe CMS is a great PHP framework better than Laravel (the most popular at the moment) but its documenation lacking.
To expand a bit on Jonh's answer, LinkingMode is a function exposed on all objects inherited from SiteTree. Like you probably have noticed when using it, you have the values "current" (if this object is the same as the current executing page), "section" (if the page is an ancestor of the current executing page) or "link" (for anything else).
While what Greg mentioned in the comments is true, you do see this commonly used in templates to help with styling menus, you can use this wherever in your code, not just the template.
LinkingMode is backed by two other functions on a SiteTree object, isCurrent() and isSection(). As their names imply, you can use them directly to find out the state of the SiteTree object in relation to the current executing page.
At the time of writing this answer, this is the code behind LinkingMode (Silverstripe 3.3):
public function LinkingMode() {
if($this->isCurrent()) {
return 'current';
} elseif($this->isSection()) {
return 'section';
} else {
return 'link';
}
}
The title may be confusing, but I'm not sure how to word it otherwise. So I want to be able to initialize a class and call methods from it without knowing the class name before getting it from an array.
What I want to do
$modules = array(
'Forums' => array(
'class'=>'Forums',
'classVar'=>'forum'
)
);
foreach($modules as $name => $module) if ($module['enabled']) {
require_once('include/scripts/'.$module['link']);
$$module['classVar'] = new $module['class'];
$$module['classVar'] = $module['classVar'];
global $$module['classVar'];
}
However, I know that this is a roundabout way to accomplish this, and I need to know if there's an easier way, and if this is even logical.
The reason I want to do this is because what I'm working on will be able to accept modules, and I need to be able to get all of the stats from the modules and display them in the main admin panel, but there's no way to know if a module is enabled and running if I didn't create it. For instance, if someone created module.php that tracked how many times someone clicked a specific link, the software wouldn't natively know about it, and I need it to be able to get stats from it.
Hmm, you might be interested in the Singleton design pattern (http://en.wikipedia.org/wiki/Singleton_pattern and http://www.phptherightway.com/pages/Design-Patterns.html).
In this case you'd have a list of classes in your configuration array and call "getInstance()" when you actually want to do something with the actual module.
Also, have a look at the Factory pattern, it might be useful for your purposes as well.
Is there any option in moodle to pass $course in to another function which is
function sectiontext_survey_display($data) {}
in to
function sectiontext_survey_display($data, $course) {}
Actually I just wanna filter some items on the basis of course ID and Category
Looks like Questionnaire is an activity plugin rather than part of the core Moodle
https://moodle.org/plugins/view.php?plugin=mod_questionnaire
I'm guessing you could try something like this to get the course id
$coursed id = $this->course->id;
Finally found solution :)
global $course;
var_dump($course)
Oddly enough, sectiontext_survey_display appears to be an unused, private function within the questionnaire addon (at least within the version that I have looked at) - I'm not sure that extending it will be very helpful.
Unfortunately, the class in question links to a survey, which can be used in multiple instances of the questionnaire module, which means that the same survey could be used in multiple courses. There is no way you can get directly from the questionnaire_question class back to a course.
The best you can manage (and, in reality this will probably always work), is to use either:
global $COURSE;
or
global $PAGE;
$PAGE->course;
Both of these should work (although the second is encouraged in more recent versions of Moodle).
I'm looking for a way to prevent repeated calls to the database if the item in question has already been loaded previously. The reason is that we have a lot of different areas that show popular items, latest releases, top rated etc. and sometimes it happens that one item appears in multiple lists on the same page.
I wonder if it's possible to save the object instance in a static array associated with the class and then check if the data is actually in there yet, but then how do I point the new instance to the existing one?
Here's a draft of my idea:
$baseball = new Item($idOfTheBaseballItem);
$baseballAgain = new Item($idOfTheBaseballItem);
class Item
{
static $arrItems = array();
function __construct($id) {
if(in_array($id, self::arrItems)){
// Point this instance to the object in self::arrItems[$id]
// But how?
}
else {
// Call the database
self::arrItems[id] = $this;
}
}
}
If you have any other ideas or you just think I'm totally nuts, let me know.
You should know that static variables only exist in the page they were created, meaning 2 users that load the same page and get served the same script still exist as 2 different memory spaces.
You should consider caching results, take a look at code igniter database caching
What you are trying to achieve is similar to a singleton factory
$baseball = getItem($idOfTheBaseballItem);
$baseballAgain =getItem($idOfTheBaseballItem);
function getItem($id){
static $items=array();
if(!isset($items[$id])$items[$id]=new Item($id);
return $items[$id];
}
class Item{
// this stays the same
}
P.S. Also take a look at memcache. A very simple way to remove database load is to create a /cache/ directory and save database results there for a few minutes or until you deem the data old (this can be done in a number of ways, but most approaches are time based)
You can't directly replace "this" in constructor. Instead, prepare a static function like "getById($id)" that returns object from list.
And as stated above: this will work only per page load.
I'm not quite grokking a couple of things in OOP and I'm going to use a fictional understanding of SO to see if I can get help understand.
So, on this page we have a question. You can comment on the question. There are also answers. You can comment on the answers.
Question
- comment
- comment
- comment
Answer
-comment
Answer
-comment
-comment
-comment
Answer
-comment
-comment
So, I'm imagining a very high level understanding of this type of system (in PHP, not .Net as I am not yet familiar with .Net) would be like:
$question = new Question;
$question->load($this_question_id); // from the URL probably
echo $question->getTitle();
To load the answers, I imagine it's something like this ("A"):
$answers = new Answers;
$answers->loadFromQuestion($question->getID()); // or $answers->loadFromQuestion($this_question_id);
while($answer = $answers->getAnswer())
{
echo $answer->showFormatted();
}
Or, would you do ("B"):
$answers->setQuestion($question); // inject the whole obj, so we have access to all the data and public methods in $question
$answers->loadFromQuestion(); // the ID would be found via $this->question->getID() instead of from the argument passed in
while($answer = $answers->getAnswer())
{
echo $answer->showFormatted();
}
I guess my problem is, I don't know when or if I should be passing in an entire object, and when I should just be passing in a value. Passing in the entire object gives me a lot of flexibility, but it's more memory and subject to change, I'd guess (like a property or method rename). If "A" style is better, why not just use a function? OOP seems pointless here.
Thanks,
Hans
While I like Jason's answer, it is not, strictly speaking OO.
$question = new Question($id);
$comments = $question->getComments();
$answers = $question->getAnswers();
echo $question->getTitle();
echo $question->getText();
foreach ($comments as $comment)
echo $comments->getText();
The problems are:
There is no information hiding, a fundamental principle of OO.
If the format of the answers needs to change, it must be changed in a place that is not associated with the object that houses the data.
The solution is not extensible. (There is no behaviour to inherit.)
You must keep behaviour (tightly coupled) with the data. Otherwise you are not writing OO.
$question = new Question($id);
$questionView = new QuestionView( $question );
$questionView->displayComments();
$questionView->displayAnswers();
How the information is displayed is now an implementation detail, and reusable.
Notice how this opens up the following possibility:
$question = new Question( $id );
$questionView = new QuestionView( $question );
$questionView->setPrinterFriendly();
$questionView->displayComments();
$questionView->displayAnswers();
The idea is that now you can change how the questions are formatted from a single location in the code base. You can support multiple formats for the comments and answers without the calling code (a) ever knowing; and (b) ever needing to change (to a significant degree).
If you are coding text formatting details in more than one location because you are misusing accessor methods, the life of any future maintainers will be miserable. If the maintainer is a psychopath who knows where you live, you will be in trouble.
Objects, Data, and Views
Here's the problem, as I understand it:
Database -> Object -> Display Content
You want to keep the behaviour of the object centred around logic that is intrinsic to the object. In other words, you don't want the Object to have to do things that have nothing to do with its core responsibilities. Most commonly this will include load, save, and print functionality. You want to keep these separate from the object itself because if you ever have to change database, or output format, you want to make as few changes in the system as possible, and restrain the ripple effect.
To simplify this, let's take a look at loading only Comments; everything is applicable to Questions and Answers as well.
Comment Class
The Comment class might offer the following behaviours:
Reply
Delete
Update (requires permission)
Restore (from a delete)
etc.
CommentDB Class
We can create a CommentDB object that knows how to manipulate the Comments in the database. A CommentDB object has the following behaviours:
Create
Load
Save
Update
Delete
Restore
Notice that these behaviours will likely be common across all objects and can therefore be subject to refactoring. This will also let you change databases quite easily as the connection information will be isolated to a single class (the grandfather of all database objects).
Example usage:
$commentDb = new CommentDB();
$comment = $commentDb->create();
Later:
$comment->update( "new text" );
Notice that there are a number of possible ways to implement this, but you can always do so without violating encapsulation and information hiding.
CommentView Class
Lastly, the CommentView class will be tightly coupled to a Comment class. That it can obtain the attributes of Comment class via accessors is expected. The information is still hidden from the rest of the system. The Comment and its CommentView are tightly coupled. The idea is that the formatting is kept in a single place, not scattered throughout classes that need to use the data willy nilly.
Any classes that need to display comments, but in a slightly different format, can inherit from CommentView.
See also: Allen Holub wrote "You should never use get/set functions", is he correct?
Why pass either? What about:
<?php
$question = new Question($id);
$comments = $question->getComments();
$answers = $question->getAnswers();
echo $question->getTitle();
echo $question->getText();
foreach ($comments as $comment)
echo $comments->getText();
foreach ($answers as $answer)
{
$answer_comments = $answer->getComments();
echo $answer->getText();
foreach ($answer_comments as $comment)
echo $comment->getText();
}
Where getComments() and getAnswers() use $this->id to retrieve and return an array of comment or answer objects?
You could build utility methods in the comment and answer objects that allow you to load by parent id. In which case, just taking an id as a parameter would be nice.
$question = new Question($id);
$answers = Answer::forQuestion($question->id);
$comments = Comment::forQuestion($question->id);
$ans_comments = Comment::forAnswer($answer->id); // or some way to distinguish what the parent object is.
Edit: Likely the child model (Comment or Answer in this case) doesn't need anything from the parent except and id to do db queries with. Passing in the entire parent object would be overkill. (Also, PHP has a terrible time garbage collecting objects with circular references, which might be fixed in the 5.3 series.)
Both styles are acceptable. Sometimes you only need the value, sometimes you'll need the object. In this example I would personally do something along the lines of your first example, but trivial programs like this don't tend to exist in the wild very often so maybe you want the second piece.
My rule of thumb is to do the thing in the least number of lines that still clearly demonstrates what you're attempting to do to anyone who comes after you. The overhead of most object creation vs value passing is something you'll likely never ever have to deal with on modern arch.
Adding to what #jasonbar already mentioned:
I don't know when or if I should be passing in an entire object, and when I should just be passing in a value.
It depends on the Coupling you need and the Cohesion you desire.
Passing in the entire object gives me a lot of flexibility, but it's more memory and subject to change.
PHP does not copy the object when you use it as an argument to a function. Neither do most other languages (either by default, like C# and Java, or upon explicit request, like C and C++)
To add to Dave Jarvis and jasonbar answers, I usually have DataMappers to convert between relational data and objects, instead of using an ActiveRecord approach. So, following your example, we would have these classes:
Question
Answer
Comment
and their data mappers:
QuestionMapper
AnswerMapper
CommentMapper
Each mapper implementing a similar interface:
save(object) // creates or updates a record in the database (or text file, for that matter)
delete(id)
get(id)
Then, we would do as:
$q = QuestionMapper::get( $questionid );
// here we could either (a) just return a list of Answers
// previously eagerly-loaded by the
// QuestionMapper, or (b) lazy load the answers by
// calling AnswerMapper::getByQuestionID( $this->id ) or similar.
$aAnswers = $q->getAnswers();
foreach($aAnswers as $oAnswer){
echo $oAnswer->getText();
$aComments = $oAnswer->getComments();
foreach($aComments as $oComment){
echo $oComment->getText();
}
}
Regarding the use of things like QuestionView->render( $question ), I prefer to have Views which display the data using getters from the domain objects. If you pass a Question to a HTMLView, it will render it as HTML; if you pass it to a JSONView, then you'll get JSON-formatted content. This means that the domain objects need to have getters.
PS: We could also consider the QuestionMapper to load everything related to Questions, Answers, and Comments. Since Comments always belongs to Answers or Questions, and Answers always belong to Questions, it could make sense that the QuestionMapper loaded everything. Of course we would have to consider different strategies for lazy loading a Question's set of Answers and Comments, to avoid hogging the server.