/sunrise

Code-driven Forms and Fields Library for WordPress, Built for Agencies

Primary LanguagePHP

#DEPRECATION NOTICE

Sunrise has been deprecated in favor of WPLib.

#Sunrise

Forms & Fields for Post Types in the WordPress Admin

NOTE: This is a DEAD project that never really got off the ground. Its planned features will eventually be added to WPLib.

##CONTENTS

##Who is Sunrise For? Sunrise is for site builders, not end-users. To use Sunrise requires understanding PHP, although it takes very little PHP expertise to use Sunrise effectively. If you can correctly call the WordPress function register_post_type() in an 'init' hook then you can add Forms and Fields to your post types using Sunrise.

More specifically, Sunrise was designed for use by development teams within agencies, corporations and government who are building WordPress-based websites for external and internal clients. That's our use-case and so we have built a tool designed to address our needs as well as to make easier the jobs of others in our same roles.

##IMPORTANT!

  • Sunrise is currently alpha-level software and thus it's API will change before it is released as Sunrise v2.0 (Don't say we didn't warn you!)
  • Many of the glossary terms are tentative; if you have ideas for better terms then please propose your suggestions using the GitHub issue tracker.

##Background For those who care about this pre-history of Sunrise read on. Otherwise, jump to the QuickStart.

###Conception and Sunrise-1 The concepts for Sunrise were developed in numerous projects and at least one CMS product over several years starting in mid 2010. That culminated in a heavily legacy version that we have on BitBucket as Sunrise-1. Although we've published it publicly, it was never really released.

Sunrise-1 was driven by client requirements which meant refactoring was rarely allotted the time necessary and is thus highly over-architected. But, we learned a lot of lessons while building Sunrise-1.

###Reimaging and this Sunrise. This implementation of Sunrise is light and tight compared to Sunrise-1, and it includes all the things we've learned about design of API libraries for WordPress since we started extending WordPress in 2010.

This Sunrise was built starting mid-February 2014 for a two-fold purpose:

  1. For the feature needs of the pending relaunch of PM-Sherpa,
  2. As an architecture study for the WordPress Metadata team [and on GitHub], and
  3. As as proposed best-practice for WordPress plugin/theme/library architecture at HardcoreWP.com and for potential presentation at WordCamps and instruction at WordPress training courses.

This Sunrise will be released out of beta as Sunrise v2.0.

###About This Sunrise and WordPress' Early Load sunrise.php File

This Sunrise has no relationship with the sunrise.php file recognized by WordPress during bootstrap. And as this Sunrise won't conflict at a technical level with WordPress' sunrise.php we're keeping the name we picked back in 2010 before we knew such a filename in the /wp-content/ directory was special to WordPress. Because we like the name.

##QuickStart

###1. Installing Sunrise While Sunrise can be used as a plugin in the /wp-content/plugins/ directory it's really meant to be required by a website and thus should be added to a /sunrise/ directory within /wp-content/mu-plugins/.

Once you've added Sunrise into its own directory within the must-use plugins directory you'll also need a plugin loader file to load it. Here's one we use as /wp-content/mu-plugins/plugin-loader.php:

<?php
/**
 * Plugin Name: {Your Site}'s Must-Use Plugins
 * Description: Contains the WordPress/PHP plugins for {Your Site}.
 */
require(__DIR__ . '/sunrise/sunrise.php');

###2. Registering Forms and Fields: Once you've got Sunrise installed you only need to register your Forms and Fields in an 'init' hook. The following will add the three (3) fields 'website', 'tagline' and 'blurb' to the Add New/Edit page in the admin for the 'pm_solution' custom post type:

<?php 
/**
 * functions.php - The functions file for {Your Site}'s theme.
 */
add_action( 'init', 'yoursite_init' );

function yoursite_init() {
  
  Sunrise::register_form( 'pm_solution' );
  
  Sunrise::register_form_field( 'website', array(
    'type'  => 'url',
    'label' => __( 'Website', 'pm-sherpa' ),
    'html_placeholder' => 'http://www.example.com',
    'html_size'  => 50,
  ));
  
  Sunrise::register_form_field( 'tagline', array(
    'label' => __( 'Tagline', 'pm-sherpa' ),
    'html_size'  => 50,
  ));
  
  Sunrise::register_form_field( 'blurb', array(
    'type'  => 'textarea',
    'label' => __( 'Blurb', 'pm-sherpa' ),
  ));
}

NOTE: we assume you already called register_post_type() to register the 'pm_solution' post type in another 'init' hook somewhere else in your code.

###3. There is No Step 3! That's it, you're done! With the previous code in your theme's functions.php file (or better yet in your own plugin) you would now have fields that look like so:

Of course we don't actually recommend using global functions for your hooks. Instead see this blog post to learn how to use classes as code wrappers for your WordPress Plugin.

##Usage Sunrise is designed to be as simple as possible to use yet still provide headroom for when the client says: "But it must work exactly like this!" ###Registering a Form @todo... ###Registering a Field to a Form @todo... ###Registering a Multiform Field @todo... ###Adding a Multiform Field to a Form @todo... ###Registering and Adding Multiform Fields @todo... ###Registering and Using Field Prototypes @todo... ###Developing and Registering New Field Types Classes. @todo...

##Architecture

###The Sunrise Class The Sunrise API is effectively controlled through the Sunrise class as a namespace along with several static methods. The static methods are known as Helper Methods and they can be called using this general syntax:

  • Sunrise::method_name( <arg1>, <arg2>, ... <argN> )

####Main Sunrise Class Methods The following methods are the ones we expect will be used most often:

Method Name Description
register_form() Registers a Form which is a container of fields. Forms are uniquely identified by their Object Type, Form Context and Form Name. See reference. @todo...
register_form_field() Registers a Field for the most recently registered Form. See reference. @todo...
register_field() Registers a Multiform Field that can be added to multiple forms using the add_form_field() method. See reference. @todo...
add_form_field() Adds a previously registered Multiform Field to the most recently registered Form. See reference. @todo...

###The $args Pattern Sunrise makes heavy use of what we call "The $args Pattern". This pattern can be found in use in many aspects of the WordPress core code but by no means in all areas of WordPress core where it could be used.

@more...

###Object Types @todo...

###Rules of Unqualified Object Type Evaluation When Object Types are provided as strings they can either be in the format:

  • "{$object_type}/{$subtype}" or
  • "{$unqualified_object_type}"

For example:

  • "post/page" or
  • "my_custom_post_type" or
  • "comment"

Sunrise understands that "my_custom_post_type" expands to "post/my_custom_post_type" because "my_custom_post_type" is not one of the Reserved Unqualified Object Types.

Conversely "comment" expands to "comment/" because it is one of the Reserved Unqualified Object Types and (currently) WordPress does not support the concept of a subtype for a Comment.

####Reserved Unqualified Object Types Sunrise plans to allocate these names as Reserved Unqualified Object Types but expects to possibly add more before the version of Sunrise is released as non-beta:

  • User
  • Comment
  • Option
  • Term
  • Taxonomy
  • Site
  • Network

###Late Fixup and Instantiation Unlike register_post_type() and register_taxonomy() the methods for registering Forms and Fields in Sunrise delay most Fixup and most instantiation until the latest point in page load possible. And for places where it is possible to avoid Fixup and instantiation Sunrise bypasses Fixup and instantiation, such as the instantiation of Fields not used in any current Forms needed for the current page load.

@more...

###Uniquely Identifying Forms Unlike Fields which can be uniquely identified with either their field_name, if they are Multiform Fields, or by a Form and their field_name Forms do not have a single unique identifier other than the form_index assigned at the time the Form is registered.

The register_form() method returns the registered form_index property which can be passed to register_form_field() if needed to override the Current Form

examples...

###Naming Consistent and rigorous naming is a critical part of the Sunrise architecture. In many but not all cases the naming drives functionality so that in those cases the naming conventions must be used consistently in order for code to work correctly.

@more...

Property Name Prefixes

We have decided to prefix most of the properties of a class with the class' VAR_PREFIX; i.e. for Forms it's 'form_' and for Fields it is 'field_'. We do this in all cases that a field name otherwise would not have an underscore but we leave compound property names alone, i.e. $field->form_name vs. $field->object_type.

However in special cases where the prefixed name just does not feel right we add the property names to the class' NO_PREFIX constant using a pipe character ('|') as a separator. Examples here include $forms->_fields (an internal property of the Form), and $field->value.

We use the prefixed names like $field->field_name instead of the simpler $field->name for two (2) reasons:

  1. It makes search and replace easier thus reducing errors introduced during inevitable code refactoring, and
  2. It enables values for multiple objects to be mixed within the same $args array, i.e. field_ or no prefix for the Field itself, html_ as a prefix for those properties that are specifically for the HTML attributes of the Field's Control, label_ for Label specific properties, label_html_ for the HTML properties of the Label's <label> element, and so on.

To reduce the burden of having to repeatedly type 'field_' as a prefix when registering fields, and to reduce the resultant visual noise we allow you to drop the field_ as keys in your $args arrays used for initialization unless you need to remove a rare ambiguity.

We also (plan to) implement VAR_ALIASES to allow certain properties such as 'html_placeholder' to be specified as just 'placeholder' in the $args array.

Method Naming: property_name() vs. get_property_name()

Many libraries _(including ones we developed) used the pattern get_*()/set_*() for internal property getters and setter but with Sunrise we decided to break tradition and go with something the looks simpler. So instead of get_property_name() for getters we use property_name() in most, but not all cases. For example, $field->value().

We use get_property_name() for cases where we are generally doing a good bit more work to retrieve the value than just accessing an internal instance property, at least the first time the value is retrieved.

Although theoretically classes should be black boxes and thus we theoretically should have a more consistent naming convention experience has taught us that in reality good developers simply cannot ignore that some operations are time-consuming and might have recursive side-effects. So a perfect example use of the get_*() pattern is $form->get_fields().

@more...

###Internal Property Caching @todo...

###Location of Hooks Hooks needed on page load are only adding by the core Sunrise class and by its Helper Classes. This makes autoloading of the remaining classes viable.

@more...

###Autoloading @todo...

###Validation @todo...

###Methods for Subclassing

Method Name Description
default_args() @todo...
do_assign() @todo...
pre_assign() @todo...
initialize() @todo...

@more...

###Class Constants Sunrise uses many of it's Class Constants as metadata in a manner very similar to how annotations are used in Java and how attributes are used in .NET. Sunrise's Class Constants allow base classes to inspect information about child classes and to operate differently based on those constants.

Constant Name Description
VAR_PREFIX The prefix used for Qualified Names of Property $args.
NO_PREFIX The pipe-separated list of field names not to prefix.
CONTROL_TAG HTML <tag> name used for the main HTML element of the Control Feature.
HTML_TYPE HTML @type attribute implemented for a Field class.
FORM_CONTEXT Context of Form: 'admin' or 'theme'

@details...

Basically we could have used WordPress filters instead of Class Constants but in the few cases we use them Class Constants are used to both improve runtime performance and simplify the implementation of child classes for Forms, Fields, Features.

###The Sunrise_Base Class @todo...

###Target Platforms

  • Sunrise is currently designed to target HTML5 without losing too much functionality in HTML4,
  • Sunrise requires PHP 5.3 or later because it uses get_called_class() to provide Helper Classes and Methods. Note that this is not critical to the architecture but would require a build step using Grunt or similar given the current source file structure and layout.

##Class Reference @todo...

Class Name Description
CORE CLASS ---
Sunrise @todo...
SUPPORTING CLASSES ---
Sunrise_Object_Classifier @todo...
Sunrise_Html_Element @todo...
Sunrise_Metabox @todo...
FORM CLASS(ES) ---
Sunrise_Post_Admin_Form @todo...
FIELD CLASSES ---
Sunrise_Text_Field @todo...
Sunrise_Textarea_Field @todo...
Sunrise_Url_Field @todo...
FEATURE CLASSES ---
Sunrise_Control_Feature @todo...
Sunrise_Label_Feature @todo...
Sunrise_Help_Feature @todo...
Sunrise_Infobox_Feature @todo...
Sunrise_Message_Feature @todo...
HELPER CLASSES ---
_Sunrise_Forms_Helper @todo...
_Sunrise_Fields_Helper @todo...
_Sunrise_Html_Elements_Helper @todo...
_Sunrise_Post_Admin_Forms_Helper @todo...
_Sunrise_Posts_Helper @todo...

##Glossary Sunrise takes great pains to name aspects of its architecture and to be very consistent with the way in which those names are used. So here is the

  • Form - A conceptual wrapper for a collection of Fields.

    • In some (future) contexts a Form may be associated with an HTML <form>, such as when it's Form Context is theme, but in other cases, such as the only one currently implemented a Form is simply a collection of Fields where there may be more than one Sunrise Form within a single HTML <form>.
    • For Posts in the Form Context of admin (which is currently the only use-case) a Form may either be displayed between the title/URL and the TinyMCE rich text editor for the post content or within a WordPress metabox. Forms with the default Form Name of 'main' are to be displayed above post content and the others in metaboxes.
  • Field - A container of Features that collectively represent an item of data or information that should be persisted for an Object when the Object is persisted to the database (or wherever else it might be persisted in the future.)

    • Fields are comprised of a collection of Features: Control, Label, Help, Infobox and Message.
  • Feature - An aspect of a Field that provides some functional part to the field. At the point only Control and Label are implemented.

    • Control - A composition of HTML elements that are collectively used to present a data entry field, typically an <input>, <select> or <textarea>.
    • Label - A composition of HTML elements that are collectively used to present a <label> associated with a Control above or to the left of the Control.
    • Help - (pending) A composition of HTML elements that are collectively used to present persistent help text for a field to the user, typically below the field.
    • Infobox - (pending) A composition of HTML elements that are collectively used to present non-persistent help text for a field to the user, typically on-click or on-hover of an "info" icon.
    • Message - (pending) A composition of HTML elements that are collectively used to present a warning or error message on field validation.
  • Object - Generic term used to refer collectively to named well-known entities withing WordPress such as Post, User, Comment, Taxonomy, Term, etc. Currently Sunrise only supports Posts but plans to support the others in the near future.

  • Object Type - A classifier for an object type that has both object_type and subtype properties. An Object Type can be representing using a string of the format "{$object_type}/{$subtype}" or as an instance of the Sunrise_Object_Classifier i.e. 'post/post', 'post/page', 'post/pm_solution' and 'user/'.

    • The class constructor accepts a string formatted to represent an Object Type and the constructor by delegation parses it into the two (2) properties $classifier->object_type and $classifier->subtype.
    • The class also has a __ToString() method that allows the Object Type's value to be cast to a correctly formatted string when that is needed.
  • Qualified Object Types - A classifier string that contains does not contain a slash but still uniquely identifies the Object Type by following rules for evaluation. See the section titled "Rules of Unqualified Object Type Evaluation."

  • Unqualified Object Types - A classifier string that does not contain a slash but still uniquely identifies the Object Type by following rules for evaluation. See the section titled "Rules of Unqualified Object Type Evaluation."

  • Form Context - A simple string identifying the targeted location for the form, i.e. 'admin' or 'theme'. Together with the Form's Object Type and form name the Form Context gives Sunrise enough information to determine where the Form would be presented to the user.

  • Static Hooks - Although they may sound exotic Static Actions and Static Filters are simply a shorthand for regular WordPress actions and filters that use a static methods of a class for the callback.

    • For example, the following two lines have the exact same result but the first looks a lot cleaner, in our opinion at least:

      Sunrise::add_static_action( 'init' );
        add_action( 'init', array( \_\_CLASS\_\_, '_init' ) );
    • Note that using the add_static_action() method enforces an underscore prefix on methods to indicate the method should be considered non-public for external users of the class. For more on Static Actions and Filters such as how to handle priorities other than 10 and how to change the hook name see details.

  • Multiform Field - Fields in Sunrise are either specific to a Form or they are Multiform Fields meaning they can be added to multiple forms by name. To register a Multiform Field use Sunrise::register_field() method vs. the Sunrise::register_form_field() method, the latter of which registers the Field and associates it exclusively with the most recently registered Form.

  • Helper Classes and Methods - Helper Classes are classes with only static methods designed to contribute their methods to the Sunrise class to enable the streamlined API of Sunrise. Within Sunrise all helper classes are found in the /helper/ subdirectory and are named with a leading underscore to indicate they are not designed to be referenced by name directly outside of the class file itself. To add a helper class you simple register it with Sunrise::register_helper( $class_name ); and then all it's static methods can be called via the Sunrise class. However, if you do this be sure to prefix your method names with your plugin's or theme's own unique prefix.

  • Current Form - The Current Form in Sunrise is the form most recently registered using Sunrise::register_form();. The method Sunrise::register_form_field(); adds the newly registered Field to the Current Form. Th register_form() method also returns the $form_index in case you need to later change the Current Form with Sunrise::set_form_index( $form_index );. You can also capture the Current Form's Index by calling Sunrise::form_index(); after any forms have been registered.

  • Storage - @todo...

  • Class Constants - @todo...

##Comparison To Other Solutions The main difference you'll find between Sunrise and most other Form & Field plugins/solutions is Sunrise was designed for coders who:

  1. Use version control so can't store form and field definitions in the database,
  2. Appreciate a solution with a minimal, clean and highly consistent API,
  3. Want a solution whose major architectural focus was performance,
  4. Need a fully extensible OOP-based solution that won't box them in, and
  5. Prefer a free-as-in-beer open-source solution, not one released for the purpose of selling to you, or upselling you to a Pro version.

While other solutions may address some of these points we are unaware of any others that address them add besides Sunrise.

###Custom Metadata Manager @todo... ###Custom Metaboxes and Fields for WordPress @todo... ###Advanced Custom Fields @todo... ###Types - Custom Fields and Custom Post Types Management @todo... ###MasterPress @todo... ###WPAlchemy Metabox PHP Class The WPAlchemy_MetaBox PHP class @todo... ###Custom Field Template @todo... ###Admin Page Framework @todo...