Strider64 Posted April 13, 2013 Share Posted April 13, 2013 (edited) I think I'm starting to wrap my mind around how to work with OOP - Objects and Arrays I have starting a small test php file that I'm doing testing on. Here's my main file <?php require('includes/Connect.MySQL.Class.php'); ?> <?php require('includes/TopicFileClass.php'); ?> <?php $data = array(); // New instance/modifier of class MemberTopic $topic = new MemberTopic($data); // Retrieve topics from mysqli database $topic = $topic->retrieve_record(); //print_r($topic); ?> <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>Object Oriented Programming</title> </head> <?php ?> <body> <?php foreach ($topic as $key=>$record) { // Loop through the Array to pull out the objects(records)?> <h1><?php echo $record->blog_name; // Display the Title of the Blog ?></h1> <p><?php echo $record->content; // Display the Content of the Blog ?></p> <?php } // Closes the foreach loop ?> </body> </html> An here is where I retrieve my data <?php require_once ("Connect.MySQL.Class.php"); class MemberTopic extends ConnectMySQLClass { protected $threads = array(); protected function main_record() { $database = parent::connect(); //Connects to the mysqli Database $query = "SELECT * FROM pages ORDER by id"; $result = $database->query($query); while ($page = $result->fetch_array(MYSQLI_ASSOC)) { $this->threads[] = (object) $page; } /* free result set */ $result->free(); //print_r($this->threads); return $this->threads; } public function retrieve_record() { //return $records = $this->assign_topic_record(); return $this->main_record(); } } My questions is there a way to make it so I have each object tied to a user name instead of a number? I tried doing $this->threads[$page['username']] = (object) $page; which only worked partially for it skip a few records(objects). If there isn't a way of doing it, it's no big deal for this has simplified my code a lot and makes it so much easier following what the code is doing. Thanks John Edited April 13, 2013 by Strider64 Quote Link to comment Share on other sites More sharing options...
trq Posted April 13, 2013 Share Posted April 13, 2013 is there a way to make it so I have each object tied to a user name instead of a number? I don't understand that questions sorry. How are they "tied to a number" ? Another thing I don't understand is, why does your MemberTopic class extend ConnectMySQLClass? MemberTopic is quite obviously not a database connection type. The relationship makes no sense. A massive part of OOP is dealing with object relationships. This code does not describe these relationships at all well. Quote Link to comment Share on other sites More sharing options...
ignace Posted April 13, 2013 Share Posted April 13, 2013 (edited) In OO everything is resembled by an object so your pages query (which returns blog posts?) would return Page/BlogPost objects. A basic example of this in code would be: foreach ($blogPosts as $post) { // $blogPosts is an array of BlogPost objects. $post->getTitle(); $post->getContent(); } Now this is all fine, but what if you want to both print blog posts on an HTML page and an RSS feed? RSS does not understand html entities (unless you add it in through a custom doctype). So you want to be able to print both in different ways depending on the context. foreach ($renderer->getStrategy('html', $blogPosts) as $post) { $post->getTitle(); $post->getContent(); Not much difference? Actually if you would inspect $post now you would see it's a BlogPostHtmlView and no longer a BlogPost. The BlogPostHtmlView: class BlogPostHtmlView { private $post; .. public function getTitle() { return htmlentities($this->post->getTitle()); } public function getContent() { return htmlentities($this->post->getContent()); } } Whereas the BlogPostRssView: class BlogPostRssView { private $post; .. public function getTitle() { return new DOMCDataSection($this->post->getTitle()); } public function getContent() { return new DOMCDataSection($this->post->getContent()); } } That said because this code is the same for all posts you could make a flyweight out of it. class BlogPostRssView { private $posts; private $post; .. public function seek($pos) { $this->post = $this->posts[$pos]; } public function getTitle() { return new DOMCDataSection($this->post->getTitle()); } public function getContent() { return new DOMCDataSection($this->post->getContent()); } } In OO it's important to understand what the responsibility is for each object and more important what isn't. Rule of thumb, if they own the data, they are responsible. That said just because they own the data does not mean they are also responsible for loading/storing themselves in a database because you want some flexibility here and also be able to load them from for example an XML or simply an array during testing. So, we put that responsibility in another object. Also sometimes the responsibility is dependent upon language limitations for example in Java they have constructor polymorphism and constructing an object in different valid states is possible using multiple constructors, in PHP you only have one, so most opt to use a Factory Method to work around that problem, but more often then not this will pollute your object and create hard dependencies in different areas of your code, and since these are mostly business domain objects they change ALOT. For example at some point you may have a Special Case so now your original object would be responsible for creating other objects.. In these cases it's best to simply create a Factory and a Special Case will no longer bother you. Edited April 13, 2013 by ignace Quote Link to comment Share on other sites More sharing options...
Strider64 Posted April 13, 2013 Author Share Posted April 13, 2013 (edited) I don't understand that questions sorry. How are they "tied to a number" ? Another thing I don't understand is, why does your MemberTopic class extend ConnectMySQLClass? MemberTopic is quite obviously not a database connection type. The relationship makes no sense. A massive part of OOP is dealing with object relationships. This code does not describe these relationships at all well. It was kind of late when I wrote for I meant by number is making the index of the array be associative instead of numeric. However, after reading these replies that doesn't make sense now to me either. , I think I now understand it is more important to understand what I want the class/methods to do in OOP and take one step at a time. I thought in order for MemberTopic class to have access to the mysqli it would have to extend ConnectMySQL Class, and after researching what you mean I found out that it was sloppy code on my part for I have a separate file with the extend ConnectMySQLClass and I should had never had require_once ("Connect.DB.Class.php"); in the other file. Thanks for Replying <?php require_once ('common.php'); abstract class ConnectMySQLClass { protected static function connect() { $database = new mysqli(DB_HOST,DB_USERNAME,DB_PASSWORD, DB_NAME); return $database; } } Edited April 13, 2013 by Strider64 Quote Link to comment Share on other sites More sharing options...
Strider64 Posted April 13, 2013 Author Share Posted April 13, 2013 In OO everything is resembled by an object so your pages query (which returns blog posts?) would return Page/BlogPost objects. A basic example of this in code would be: foreach ($blogPosts as $post) { // $blogPosts is an array of BlogPost objects. $post->getTitle(); $post->getContent(); } Now this is all fine, but what if you want to both print blog posts on an HTML page and an RSS feed? RSS does not understand html entities (unless you add it in through a custom doctype). So you want to be able to print both in different ways depending on the context. foreach ($renderer->getStrategy('html', $blogPosts) as $post) { $post->getTitle(); $post->getContent(); Not much difference? Actually if you would inspect $post now you would see it's a BlogPostHtmlView and no longer a BlogPost. The BlogPostHtmlView: class BlogPostHtmlView { private $post; .. public function getTitle() { return htmlentities($this->post->getTitle()); } public function getContent() { return htmlentities($this->post->getContent()); } } Whereas the BlogPostRssView: class BlogPostRssView { private $post; .. public function getTitle() { return new DOMCDataSection($this->post->getTitle()); } public function getContent() { return new DOMCDataSection($this->post->getContent()); } } That said because this code is the same for all posts you could make a flyweight out of it. class BlogPostRssView { private $posts; private $post; .. public function seek($pos) { $this->post = $this->posts[$pos]; } public function getTitle() { return new DOMCDataSection($this->post->getTitle()); } public function getContent() { return new DOMCDataSection($this->post->getContent()); } } In OO it's important to understand what the responsibility is for each object and more important what isn't. Rule of thumb, if they own the data, they are responsible. That said just because they own the data does not mean they are also responsible for loading/storing themselves in a database because you want some flexibility here and also be able to load them from for example an XML or simply an array during testing. So, we put that responsibility in another object. Also sometimes the responsibility is dependent upon language limitations for example in Java they have constructor polymorphism and constructing an object in different valid states is possible using multiple constructors, in PHP you only have one, so most opt to use a Factory Method to work around that problem, but more often then not this will pollute your object and create hard dependencies in different areas of your code, and since these are mostly business domain objects they change ALOT. For example at some point you may have a Special Case so now your original object would be responsible for creating other objects.. In these cases it's best to simply create a Factory and a Special Case will no longer bother you. Thanks for replying and I definitely saving this for my notes so I can refer to it from time to time. Quote Link to comment Share on other sites More sharing options...
ignace Posted April 13, 2013 Share Posted April 13, 2013 (edited) Having a solid understanding of design patterns, GoF and Martin Fowler are highly recommended, will give you more options during the design phase. Sometimes you look at a problem and you can't quite put your finger on it, simply because you can't name it, and the weird names you come up with make it feel wrong, which means you'll be doing revision after revision and the final design is not always the best. Sometimes you only fully understand a problem when you are able to put a name or analogy on it. And not everyone has the luck of working with very talented colleagues so your mostly on your own trying to come up with a solution for which your colleagues won't hate you. Edited April 13, 2013 by ignace Quote Link to comment Share on other sites More sharing options...
Solution Strider64 Posted April 17, 2013 Author Solution Share Posted April 17, 2013 I just have to say I think I finally getting the hang of OOP and the power is has, I am truly amazed For example public function commentPostRecords() { $database = parent::connect(); //Connects to the mysqli Database $query = "SELECT id, username, pages_id, content, post_date FROM comments ORDER by id"; $result = $database->query($query); while ($page = $result->fetch_array(MYSQLI_ASSOC)) { $this->send_comments[] = new BlogPosts($page); // Assign new object BlogPosts to an array } /* free result set */ $result->free(); return $this->send_comments; } and this <?php class BlogPosts extends BlogPost { public function __construct($page) { foreach ($page as $key => $value) { $pos = strpos($key, 'date'); // Find the word date in array if ( $pos !== false ) $value = date('F j, Y g:i A', strtotime( $value )); // Format MySQL date to proper format $this->$key = $value; // Assign value to variable in object. } } } Would had taken me way more lines doing it the procedural way and the nice feature is that I can use the constructor for multiple tables. I have to say Thanks once again to ignace and trq, plus php.net manual, it truly helps visiting that site to read up on arrays, objects, etc.... Quote Link to comment Share on other sites More sharing options...
Hall of Famer Posted April 17, 2013 Share Posted April 17, 2013 Thats really great to know, congratulations. In a perfect script everything is an object, you are making a huge step into writing a professional application. If I may suggest, your next step can be building a framework with object oriented design. This way your entire application as a whole is also object oriented, which makes you further appreciate the beauty of OOP. Quote Link to comment Share on other sites More sharing options...
ignace Posted April 17, 2013 Share Posted April 17, 2013 (edited) <?php class BlogPosts extends BlogPost { public function __construct($page) { foreach ($page as $key => $value) { $pos = strpos($key, 'date'); // Find the word date in array if ( $pos !== false ) $value = date('F j, Y g:i A', strtotime( $value )); // Format MySQL date to proper format $this->$key = $value; // Assign value to variable in object. } } } namespace Blog; class Comment { private $id; private $author; private $postedDate; private $comment; .. public function setAuthor(CommentAuthorInterface $author) { $this->author = $author; } public function setPostDate(DateTime $date) { $this->postedDate = $date; } } /* interface for authoring */ interface CommentAuthorInterface { public function getDisplayName(); public function getIpAddress(); } class Guest implements CommentAuthorInterface , .. {} class User implements .., CommentAuthorInterface, .. {} class Pingback implements CommentAuthorInterface, .. {}This is true OO code using abstraction, polymorphism, and inheritance. And generally what you should strive for to create if you want to really use the advantages of OO. The first step in better OO is stopping to think about a database, it no longer exists to you. You have to become agnostic in terms of a database. Create an OO-model that works for you and leave the database for an after-thought. Your above code ties your database heavily to your code. What you do above would normally be done by a Mapper. abstract class AbstractMapper { public function hydrate(Array $data, $object) { foreach ($data as $key => $value) { $this->callSetter($object, $key, $this->getType($key)->convert($value)); } } abstract protected function getType($name); } class CommentMapper extends AbstractMapper { public function getAll() { $rows = $this->db->getAll(); foreach ($rows as $key => $row) { $this->hydrate($row, $this->factory->createEmptyInstance()); } } protected function getType($name) { if ($name === 'date') { return new DateTimeConverter; } return new ScalarConverter; } }And again this code uses abstraction to solve the problem of converting a row to an object and vice versa, albeit in a very simple fashion. This works for simple objects but not for objects having relations (which are most objects). To achieve your goal here you need something like Doctrine, which does the heavy ORM (object-relational mapping) lifting for you. Edited April 17, 2013 by ignace Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.