I need a little bit of help understanding PHP Classes.
I'm ok with the code but its more the implementation, what to contain in a class, that I'm struggling to get to grips with.
Using a blog as an example site, on the homepage (or posts page) I'm going to have a list of multiple posts.
Would I:
A: Create a class to get the details for a single post and call that within a loop on index page.
B: Create a class that gets and returns all the posts for the page and each indivdual posts details within the same class.
C: Create a class to get the list of post for the page and a seperate class to get the indiviudal post details.
My gut is telling me its C, so I can reuse the individual post class on other parts of the site (ie individual post page) and resue the class to get all the post in other ways (maybe for feature posts or similar).
Any tips welcome.
Thanks in advance!
Technically, one class should do. No need for Multiple Classes that does essentially the same thing (with a very slight alteration). Consider the Empty Class below:
<?php
class Post{
protected $post;
protected $lastPost;
const POST_TYPE_SINGLE = 1;
const POST_TYPE_LIST = 2;
public function getPostList(){
// IMPLEMENTATION CODE
// TO GET ALL THE POSTS EXISTING WITHIN YOUR SYSTEM...
// NO HTML RENDERING...
}
public function getPostByID($postID){
// IMPLEMENTATION CODE
// TO GET RAW DATA FOR THE POST WITH THE ID $postID
// NO HTML RENDERING...
}
public function getDetailedView($post){
// IMPLEMENTATION CODE
// TO GET ONE SINGLE POST AS A HTML RENDER USING THE $post...
}
public function getListView($posts){
// IMPLEMENTATION CODE
// TO GET ALLS POSTS AS A HTML RENDER USING THE $posts...
}
public function getPostsByDate($date) {
// IMPLEMENTATION CODE
// TO GET ALL THE POSTS POSTED ON THE SAME DATE: $date..
}
public function getPostByAuthor($author){
// IMPLEMENTATION CODE
// TO GET ALL THE POSTS POSTED BY THE SAME AUTHOR: $author...
}
public function render($post, $postType=1){
// IMPLEMENTATION CODE
// TO RENDER THE POST AS HTML STRING TO BE DISPLAYED AT THE FRONT-END...
// COULD BE USED TO RENDER BOTH DETAILED AND LIST VIEWS DEPENDING ON
// THE VALUE OF $postType...
if($postType == self::POST_TYPE_SINGLE){
// CODE FOR HTML SINGLE POST RENDER
}else{
// CODE FOR HTML LIST-VIEW RENDER
}
}
}
Related
I am currently trying to retrieve a list of related posts on the post article page (post single). I have created a new function within /Block/Post/ListPost.php
public function getRelatedPosts()
{
$posts = $this->getPosts();
die($this->getCategoryId());
return $this->_postCollection;
}
However when I try and output getCategoryId, I get nothing. I'm also unsure how I apply a category filter to the post collection.
Can anyone advise here?
I'm not sure where you have got the getCategoryId method from but this is not part of the ListPost block class so will not work. You cannot just invent methods like this.
You should check the block class for what methods are available. An easy way to do that without even loading the file is to add the following PHP to the class:
echo sprintf('<pre>%s</pre>', print_r(get_class_methods($this)));
exit;
You don't specify in what way the posts should be related but I'm guessing you want to get posts from the same category. One option to do this would be to load the primary category of the post and then get a post collection based on this. If you look in the Post class file, you will see the getParentTerm($taxonomy) method.
if ($category = $post->getParentTerm('category')) {
$relatedPosts = $category->getPostCollection();
// Remove the current post from the collection
$relatedPosts->addFieldToFilter('ID', array('neq' => $post->getId()));
}
You should always look at the class file's you're working with. That is the beauty of open source. You can literally see what methods are available for each object and you can even see how they work.
Suppose i'd like to display title of latest articles.
for particular Views , we use controllers to handle that. but for common section of pages like header or footer ,
How to display data(latest articles) from database (within MVC rules) ?
Note: I use php
Please check my approach:
app class:
<?php
class app{
public static function appLoader($app){
include 'apps/'.$app.'/'.$app.'.class.php';
new $app;
}
}
test.class.php :
class test extends app{
function __CONSTRUCT(){
return 'Hello World';
}
}
footer.php:
<footer>
<?php echo app::appLoader("test") // returns 'Hello World' ?>
</footer>
Note: This is w.r.t .Net MVC
If you are following MVC pattern - then there's a concept of "Partial Views" - which is just like user controls, and it can be placed in the main page.
Partial view is also an html page which might just div, no html body head etc because it will be rendered inside main html page
You might need to verify the syntax for Partial Views with PHP. The concept remains same for MVC.
There are various ways to display partial views.
One popular way is the one where Partial view is called by its action method - which will ultimately display the result(the partial view).
The Action method will return a "_Footer" Partial view - where you can put your HTML Code of displaying the data from DB(the latest articles).
The partial view must bind from the list of articles. which is popularly known as Strong Type Binding in .Net - which is nothing but mapping the view(HTML page) to a specific class to display the data from that class.
For your reference the below example can be referred(in .Net):
Create a partial view for footer(_Footer) and call it using Action Method(RenderAction - .Net). This action method can fetch the data from database and display in the partial view(_Footer)
The call to the action method be like from the view(html page):
#{ Html.RenderAction("Index", "Footer", new { Area = "Common", id}); }
And the Controller and action method like:
public class FooterController : Controller
{
public ActionResult Index(int id)
{
var vm = new FooterViewModel
{
Id = id
};
return this.PartialView("_Footer", vm);
}
}
This being the worst title imaginable, let me explain my concern as best as I can. So basically in my framework I use URLs of the type site.com/controller/method/parameters
I'll take this page for example site.com/news/edit/12
Then, if I apply no routing, my controller's name has to be News, the called method will be edit with a single parameter 12. Then if, for the sake of demonstrating my concern, I decide to do editing of the news title and body in different pages, I would have to navigate to site.com/news/edit/12/title. Which is where it gets messy and is basically what I want to ask:
What is the proper way of including pages that depend on parameters passed to the controller?
IMHO this looks pretty ugly, and what if I have further more such page separations down the page tree
class News {
public function edit($id, $section){
if(method_exists($this, $section){
return $this->$section($id);
}
}
private function title($id){
// Display page to edit news title
}
private function body($id){
// Display page to edit news body
}
}
Not to mention that this can also cause potential problems with calling existing methods that are not supposed to be called.
Please don't execute methods dynamically based on get params. This can be very dangerous.
One simple way with standard routing:
// site.com/news/edit-title/12
public function editTitle($id);
// or depending on your framework
public function edit_title($id);
Another perhaps more cleaner solution would be to build your custom routes:
// site.com/news/edit/title/12
public function editTitle($id);
What is considered the correct place for my pagination to live when using a service/datamapper/domain object trio?
Example:
Fetch ports with a few given criteria
Paginate the results
Know what page we are on, number of pages in total, number of results etc.. from the view class
The below is just something I wrote here and now, but it is similar to my application.
class PostController extends Controller
{
function viewPosts()
{
return $this->serviceFactory
->build('post')
->getPosts($aCriteria, $this->request->_get('page'), 10);
}
}
I am currently both calculating and storing pagination parameters in each service. Note that I am not pushing any data to the view from my controller.
class PostService extends AbstractService
{
public $posts;
public $iTotalPages; // This does not belong here does it?
function getPosts($aCriteria, $iPage, $iItemsPerPage)
{
$mapper = $this->dataMapperFactory->build('post');
// I do not know where to put the below
// (The parameters and the code itself)
$iCount = $mapper->count($aCriteria);
$iOffset = $iItemsPerPage * $iPage;
$this->iTotalPages = $iCount / $iItemsPerPage;
$this->posts = $mapper->find($aCriteria, $iOffset, $iOffset + $iItemsPerPage);
return $this->posts;
}
}
My views have access to the same instance of the Model layer as my controller, so I could call $service->iTotalPages from the view, but that feels wrong.
class PostsView extends AbstractView
{
function viewPosts()
{
$service = $this->serviceFactory->build('post');
if(count($service->posts)>0) {
$this->template->assign_var('TOTAL_PAGES', $service->iTotalPages);
$this->template->assign_vars('POSTS', $service->posts);
}
}
}
Solutions?
1) Create a service for pagination and have the controller exchange data with the post service as required?
2) Create a helper class for pagination that each service can include? (How would such a class look like?)
3) Add generic pagination to the AbstractService?
4) Add some sort of pagination support to my Repos?
You answer is pretty good, though I have a few suggestions.
Layer location:
The pagination infrastructure should live in the data access layer, this is so that you have low level control on how the data are retrieved.
Highest invocation
The pagination's interface should be abstracted and exposed in the UI through the service, since its a front-end concern. (I think you already have this covered in your answer)
The Abstraction:
What the UI should know are the page index, number of pages, number of items per page, and total items. Not the Offset nor the limit, these are infrastructure terms that should be encapsulated.
Input: (part of your fitlers)
Search filters
Sorting filters (if necessary)
Page index.
Number of items per page. (if the UI has control, if not then this should be encapsulated)
Output:
Filtered collection
Number of pages (for navigation)
Number of total items (if necessary in the UI)
Due to lack of answers to this question I am posting the solution I came up with. Appreciate comments/additions to it if it can be improved.
// Controller method
public function viewPosts()
{
// Create service objects
$postService = $this->serviceFactory->build('post', true);
$searchService = $this->serviceFactory->build('search');
// Set the search/filter parameters
$searchService->addFilter('author_id', $this->visitor->user_id);
$searchService->setOffset($this->request->_get('offset'));
$searchService->setLimit($this->request->_get('limit'));
// Search service will call the 'find' method on $articleService
$searchService->setServiceObject($articleService, 'find');
// Search service will return a filtered collection
return $searchService->search();
}
Doing it this way I am not leaking business logic into my controller (I think), and I have a single class to do all my filtering and analysis of the returned sql data, while still keeping each specific service in control of their specific find() method and datamappers.
I am in the process of learning the MVC pattern and building my own lightweight one in PHP
Below is a basic example of what I have right now.
I am a little confused on how I should handle AJAX requests/responses though.
In my example user controller below, If I went to www.domain.com/user/friends/page-14 in the browser, it would create a User object and call the friends method of that object
The friends method would then get the data needed for the content portion of my page.
My app would load a template file with a header/footer and insert the content from the object above into the middle of the page.
Now here is where I am confused, if a request is made using AJAX then it will call a page that will do the process over, including loading the template file. IF an AJAX call is made, I think it should somehow, just return the body/content portion for my page and not build the header/footer stuff.
So in my MVC where should I build/load this template file which will have the header/footer stuff? ANd where should I detect if an AJAX request is made so I can avoid loading the template?
I hope I am making sense, I really need help in figuring out how to do this in my MVC I am building. IUf you can help, please use some sample code
/**
* Extend this class with your Controllers
* Reference to the model wrapper / loader functions via $this->model
* Reference to the view functions via $this->view
*/
abstract class Core_Controller {
protected $view;
protected $model;
function __construct(DependencyContainer $dependencyContainer){
$this->view = new Core_View();
//$this->view = $dependencyContainer->get(view);
}
public function load($model){
//load model
//this part under construction and un-tested
$this->$model = new $model;
}
}
user controller
/**
* Example Controller
*/
class User_Controller extends Core_Controller {
// domain.com/user/id-53463463
function profile($userId)
{
//GET data from a Model
$profileData = $this->model->getProfile($userId);
$this->view->load('userProfile', $profileData);
}
// domain.com/user/friends/page-14
function friends()
{
//GET data from a Model
$friendsData = $this->model->getFriends();
$this->view->load('userFriends', $friendsData);
}
}
For me, I developed a separate object that handles all template display methods. This is good because you can then ensure that all the resources you need to display your UI is contained in one object. It looks like you've isolated this in Core_View.
Then, when an AJAX call is made, simply detect that it is an AJAX call. This can be done by either making the AJAX call through an AJAX object, which then references other objects, or you can take an easy approach and simply set an extra POST or GET field which indicates an AJAX call.
Once you've detected if it's an AJAX call, define a constant in your MVC such as AJAX_REQUEST. Then, in your template/UI object, you can specify that if it's an AJAX call, only output your response text. If it isn't, proceed with including your template files.
For me, I send it through an AJAX object. That way I don't have to worry about making a single output work for both cases. When it's ready to send a response, I just do something to the manner of print( json_encode( ...[Response]... ) ).
well, it would all start with normal request which would load the initial page. there are many options as to handle this but let's say that you start with /users/friends page which would list all your friends. then each of the friends should have link to specific friend's profile -- now this is the moment where ajax could kick in and you could ajaxify links to your friend profiles - this means that instead of normal you would instead use let's say jQuery and setup click handler in a such way that
$("a").click(function(){$.post($(this).attr("href"), null, function(data){$("#content").html(data);}});
this would use "href", and upon click would make post request to your backend. at backend, if you see that it's post, then you would just return the content for that particular friend. alternatively, if you have get request, you return all - header - content - footer.
if you use technique above, make sure to properly handle the data you receive. e.g. if there are further actions that should be done via ajax, make sure to "ajaxify" the data you get back. e.g. after updating html of the content, again apply the $("a").click routine.
this is just trivial example, to kick you off, but there are many more sophisticated ways of doing that. if you have time, I suggest reading some of agiletoolkit.org, it has nice mvc + ajax support.
You will need to use a different view. Maybe something like:
funciton friends() {
$this->view = new Ajax_Request_View();
$friendsData = $this->model->getFriends();
$this->view->load($friendsData);
}