Jump to content
Mark
 Share


4.0 - Nodes & Content Items

Reminder: this blog covers the technical details of 4.0's programming. For details on 4.0's features, follow our main blog.

Introduction

For almost all applications in the IPS Social Suite (IP.Chat being the notable exception), there are three components: Each of these different types of items share many common features. For example, in all applications you can "follow" nodes and Content Items, you can like (or give reputation on) Content Items and comments. There's also searching, tagging, moderator controls (pinning, locking, etc.), sharing, reports and so on. Up until now, applications were largely in charge of managing these different components and their relationships themselves, and utilised often complicated extensions to implement the common features. In 4.0, these components are handled differently. Each component follows an , extending a central class for the component, and implementing interfaces to enable additional features. Working with objects So, taking IP.Board as an example, the classes for each of the components (forums, topics and posts) will start off like this:

    [*]Categories created by the administrator. For example, forums in IP.Board, categories in IP.Downloads, calendars in IP.Calendar). In 4.0, the terminology used throughout the code for these is "Nodes". [*]Content created by users, usually (though, not always) within categories. For example, topics in IP.Board, files in IP.Downloads, events in IP.Calendar, images in IP.Gallery, personal conversations). In 4.0, the terminology used throughout the code for these is "Content Items". [*]Comments posted on Content Items by other users. In most applications these are simply called "comments" though in IP.Board they are called "posts" and in IP.Nexus and IP.Downloads they take the form of reviews.









    Active Record design pattern




    namespace IPSforums;
    
    class Forum extends IPSNodeModel { ... }
    
    class Topic extends IPSContentItem { ... }
    
    class Post extends IPSContentComment { ... }
    

    In , I already talked about how Nodes work and showed how easy it is to start using them. Content Items and comments are the same, with very little additional programming (only specifying a few properties to specify what database table to use and the names of the other classes they relate to), we can start using them. For example, to get a topic from the database, I just do:


    an earlier blog entry

    $topic = IPSforumsTopic::load( 1 );
    

    In this example, 1 is the ID number. If I was accepting user input, I could just wrap it in a try/catch statement. I could also, rather than using load() use an alternative factory method, loadAndCheckPerms(), which automatically checks if the currently logged in user has permission to view and throws an exception if not:


    try
    {
    	$topic = IPSforumsTopic::loadAndCheckPerms( IPSRequest::i()->id );
    }
    catch ( IPSContentNoPermissionException $e )
    {
    	IPSOutput::i()->error( "You do not have permission to view that topic.", 1, 403 );
    }
    catch ( OutOfRangeException $e )
    {
    	IPSOutput::i()->error( "Could not find topic. It may have been deleted.", 2, 404 );
    }
    

    In the object, properties match the columns from the database table. For example, to set the page title to the topic title, I just do:


    IPSOutput::i()->title = $topic->title;
    

    There's also lots of methods available. For example, to get the IPSforumsForum object of the forum the topic belongs to, I just do:


    $forum = $topic->container();
    

    Or to get the IPSMember object of the member that posted the topic, I just do:


    $author = $topic->author();
    

    An Example: Getting the latest 5 topics One thing which is particularly easier now is that now the central classes handle common functionality, you can easily obtain data without having to worry about if everything has been accommodated - the class handles it automatically. I already showed how loadAndCheckPerms() works - for a more complicated example, let's say you wanted to get the 5 most recent topics to display in the sidebar. Previously you'd have to do a query, joining on the permissions table, providing the permission mask IDs of the current user manually, remembering to check to exclude hidden topics (unless of course, the user had permission to view hidden topics). In 3.x, it would have looked something like this:






    $member = ipsRegistry::instance()->member()->fetchMemberData();
    
    $topics = ipsRegistry::DB()->buildAndFetchAll( array( 
    	'select'   => '*',
    	'from'     => array( 'topics' => 't' ),
    	'where'    => ipsRegistry::getClass('class_public_permissions')->buildPermQuery( 'p', 'perm_2' ) . ( !$member['g_is_supmod'] ? ' AND queued=0' : '' ),
    	'add_join' => array(
    		array(
    			'select'	=> 'p.*',
    			'from'		=> array( 'permission_index' => 'p' ),
    			'where'		=> 'p.perm_app="forums" AND p.perm_type="forum" AND p.perm_type_id=t.forum_id',
    			'type'		=> 'left',
    		)
    	)
    ) );
    

    In 4.0, the same thing can be done with just one line of code:


    $topics = IPSforumsTopic::getItemsWithPermission( NULL, 'start_date DESC', 5 );
    

    Naturally, one could have written a method to do this in 3.x, but in 4.0, because it is handled centrally, it is common to all applications. If a new feature is added which affects the functionality (such as when hiding content was added), each application does not have to be updated. Adding Features I already mentioned how there are certain features, like tagging, reputation, searching, etc. which are common to Nodes and Content Items throughout all applications. In 3.x, integrating these features involved writing a usually lengthy, extension. In 4.x, implementing most of these features is as simple as adding a few elements to your class. For example, let's take tagging. In 3.x, we have lengthy . It involves creating an extension, which in the IP.Board application totals 360 lines of code. In 4.x, you simply add an interface to your class - changing this:









    developer documentation for implementing tagging

    class Topic extends IPSContentItem { ... }
    

    Into this:


    class Topic extends IPSContentItem implements IPSContentTags { ... }
    

    That's all there is to it. Having done that, the form where a user adds or edits a topic will immediately gain a tags input field (the elements included on the form is handled centrally) and I can now call an additional method to get the tags on any topic so that I can display them in the HTML:


    $tags = $topic->tags();
    

    Here is a screenshot of the full developer documentation for tagging in 4.x: The programming method employed here is actually more suited to traits, as implementing the interface does not involve adding any additional code to your class. The reason we've chosen to do it this way though is because traits are only a feature in PHP 5.4 and above, and we wanted to support PHP 5.3. It is likely that in a future version of IPS Social Suite we will switch to using traits. Other examples Reporting In 3.x: - 502 lines for IP.Board. In 4.0: Read Markers (shows if content has been read or not) In 3.x: - 146 lines for IP.Board, plus manually marking items as read. In 4.0: Liking / Reputation In 3.x: - 222 lines for IP.Board. In 4.0: Following In 3.x: - 357 lines for forums, plus 361 lines for topics in IP.Board. In 4.0: For content items: For comments:













    http://www.invisionpower.com/support/guides/_/advanced-and-developers/application/application-extension-reportplugins-r100








    http://www.invisionpower.com/support/guides/_/advanced-and-developers/application/item-marking-r211








    http://www.invisionpower.com/support/guides/_/advanced-and-developers/application/application-extension-reputationphp-r101








    http://www.invisionpower.com/support/guides/_/advanced-and-developers/application/application-extension-like-r69









 Share

Comments

Recommended Comments



What's with the mixed caps? Demonstration only?

 

IPSforumsTopic

IPSNodeModel

 

IPS is capitalized out of choice.  Most classes have a capital first letter (Topic, Node, Model) but applications themselves have lower case keys (forums, downloads, etc.) which explains why you see what you do.

 

IPSNodeModel would be found in the system/ folder while IPSforumsTopic would be found in the applications/forums/ folder.  The autoloader recognizes all of this automatically.

Link to comment
Share on other sites

IPS is capitalized out of choice.  Most classes have a capital first letter (Topic, Node, Model) but applications themselves have lower case keys (forums, downloads, etc.) which explains why you see what you do.

 

IPSNodeModel would be found in the system/ folder while IPSforumsTopic would be found in the applications/forums/ folder.  The autoloader recognizes all of this automatically.

 

It's such a tiny thing... but I honestly find that rather upsetting. IPS is fine as convention, but lowercase apps in the namespace? It feels inconsistent, and I don't understand why you would go that path in the midst of a complete rearchitecture. If nothing else, why not coerce everything to lowercase or upper camel case before loading regardless?

Link to comment
Share on other sites

 

It's such a tiny thing... but I honestly find that rather upsetting. IPS is fine as convention, but lowercase apps in the namespace? It feels inconsistent, and I don't understand why you would go that path when you're doing a complete rearchitecture. If nothing else, why not coerce everything to lowercase or upper camel case before loading regardless?

 

Good question :)

 

IPS4 makes use of a (somewhat complicated) autoloader. The platform contains both classes which are part of it's core, as well as applications (Board, Gallery, Downloads, etc.) all with their own classes. If the first part of the namespace (beside the "IPS" vendor name) is lowercase, it treats it as an application key.

 

For example:

IPSforumsTopic - would be located in applications/forums/sources/Topic

IPSForumsTopic - the autoloader would look in system/Forums/Topic - which of course is the wrong place, but could theoretically exist.

Link to comment
Share on other sites

 

Good question :smile:

 

IPS4 makes use of a (somewhat complicated) autoloader. The platform contains both classes which are part of it's core, as well as applications (Board, Gallery, Downloads, etc.) all with their own classes. If the first part of the namespace (beside the "IPS" vendor name) is lowercase, it treats it as an application key.

 

For example:

IPSforumsTopic - would be located in applications/forums/sources/Topic

IPSForumsTopic - the autoloader would look in system/Forums/Topic - which of course is the wrong place, but could theoretically exist.

 

Which also raises the question... do we have to use the IPS vendor namespace in our own apps?

Link to comment
Share on other sites

Thanks for the explanation.

 

I get that this would be marginally less efficient than your current approach, but why not keep everything unified? Check for an application class, then fall back to a system class if it doesn't exist (or vice versa). Relying on case semantics for autoloading seems like a poor design choice.

Link to comment
Share on other sites

Nonono. Don't trade efficiency for avoiding lowercase. 

 

I have no proper knowledge of how much resources the one approach would have over the other, so I can't really comment on how it would affect things. Either way I would prefer efficiency to be prioritized instead. 

Link to comment
Share on other sites

So how easy is it to add a new feature to all kinds of content items or nodes? For example my content ratings app is currently handled by adding another extensions file. Would I be able to easily make a central class of my own and then implement it into current apps with hook points?

Link to comment
Share on other sites

 

Good question :smile:

 

IPS4 makes use of a (somewhat complicated) autoloader. The platform contains both classes which are part of it's core, as well as applications (Board, Gallery, Downloads, etc.) all with their own classes. If the first part of the namespace (beside the "IPS" vendor name) is lowercase, it treats it as an application key.

 

For example:

IPSforumsTopic - would be located in applications/forums/sources/Topic

IPSForumsTopic - the autoloader would look in system/Forums/Topic - which of course is the wrong place, but could theoretically exist.

 

Why not more like this?:

 

IPSForumsTopic -> applications/Forums/sources/Topic

IPSCoreForumsTopic -> system/Forums/Topic - or move core code to Core/Forums/Topic or even applications/Core/Forums/Topic

 

Seems to be a bit clearer - that way, the second part of the namespace always defines which product the file was distributed with, and is a bit easier for people new to IPS products to pick up and follow.

Link to comment
Share on other sites

 

Which also raises the question... do we have to use the IPS vendor namespace in our own apps?

 

Within the IPS framework, yes - it's "IPS" as in "IPS Social Suite" (previously we would've used "IPB").

Though we also have a PSR-0 standard autoloader if you wanted to use compliant libraries which are not part of the framework.

 

 

Thanks for the explanation.

 

I get that this would be marginally less efficient than your current approach, but why not keep everything unified? Check for an application class, then fall back to a system class if it doesn't exist (or vice versa). Relying on case semantics for autoloading seems like a poor design choice.

 

It wouldn't work. Let's say I'm trying to use IPSDb, which so far hasn't been used - the autoloader would need a database connection to check if an application exists, which it can't do, because the database class doesn't exist yet.

 

So how easy is it to add a new feature to all kinds of content items or nodes? For example my content ratings app is currently handled by adding another extensions file. Would I be able to easily make a central class of my own and then implement it into current apps with hook points?

 

Yes, you would be able to do that :)

 

 

 

Why not more like this?:

 

IPSForumsTopic -> applications/Forums/sources/Topic

IPSCoreForumsTopic -> system/Forums/Topic - or move core code to Core/Forums/Topic or even applications/Core/Forums/Topic

 

Seems to be a bit clearer - that way, the second part of the namespace always defines which product the file was distributed with, and is a bit easier for people new to IPS products to pick up and follow.

 

We're quite happy with how the autoloader works now :)

Link to comment
Share on other sites

It wouldn't work. Let's say I'm trying to use IPSDb, which so far hasn't been used - the autoloader would need a database connection to check if an application exists, which it can't do, because the database class doesn't exist yet.

 

Why database? That would be mad inefficient. Checking whether the application exists would be silly--even then the particular class being requested might not. I would just run is_file or file_exists on applications/Db/sources/... then system/Db/... if it falls through, or vice versa. I wouldn't be surprised if you already do that during the loading process just to be safe.

Link to comment
Share on other sites

Why not more like this?:
 
IPSForumsTopic -> applications/Forums/sources/Topic
IPSCoreForumsTopic -> system/Forums/Topic - or move core code to Core/Forums/Topic or even applications/Core/Forums/Topic
 
Seems to be a bit clearer - that way, the second part of the namespace always defines which product the file was distributed with, and is a bit easier for people new to IPS products to pick up and follow.

 
I have to agree with this. It's much clearer on what you're loading in and better matches PSR-0. 
 
Although I know that this isn't something that is going to change... look at this quick example using IP.Content's namespace. Currently it looks something like this:
<?php namespace MisterPhilipMyApp;

use IPScontentArticle;
use IPSContentFoobars;

class FoobarArticle extends Article implements Foobars {
    // foobar article methods
}
Look at how confusing that looks! Now, let's add in the Core namespace, since well, that's where some of our items lie:

<?php namespace MisterPhilipMyApp;

use IPSContentArticle;
use IPSCoreContentFoobars;

class FoobarArticle extends Article implements Foobars {
    // foobar article methods
}
Link to comment
Share on other sites

 

Why database? That would be mad inefficient. Checking whether the application exists would be silly--even then the particular class being requested might not. I would just run is_file or file_exists on applications/Db/sources/... then system/Db/... if it falls through, or vice versa. I wouldn't be surprised if you already do that during the loading process just to be safe.

 

You should do some is_file profiling ;)

Link to comment
Share on other sites

You should do some is_file profiling ;)

 

Profiling is for wimps. :whistle: Okay, I concede defeat, but let my displeasure be noted. I much prefer simplicity through uniformity whenever possible. If nothing else, I would rather see MisterPhilip's approach.

Link to comment
Share on other sites

Awesome stuff Mark!  Very happy to see things being standardized. :)

Is it possible at this time to talk about how easy/difficult it will be to have different Content Item types use the comment system in a different app?

 

ATM you can link IP.C Database categories (Nodes) to individual forums for linked topic creation, and IIRC you can the same in IP.Downloads.  Is this something that will be standardized as well so that ALL Nodes can be linked to an admin specified Forum to act as the discussion engine?  Having all conversations happen in the SAME place is really key to where we see things moving in the future.  

 

James

Link to comment
Share on other sites

Awesome stuff Mark!  Very happy to see things being standardized. :smile:

Is it possible at this time to talk about how easy/difficult it will be to have different Content Item types use the comment system in a different app?

 

ATM you can link IP.C Database categories (Nodes) to individual forums for linked topic creation, and IIRC you can the same in IP.Downloads.  Is this something that will be standardized as well so that ALL Nodes can be linked to an admin specified Forum to act as the discussion engine?  Having all conversations happen in the SAME place is really key to where we see things moving in the future.  

 

James

 

We haven't figured out the details yet, but yeah, there'll be some way to handle that simply.

Link to comment
Share on other sites




Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...