I've created a set of classes thatwork together to create the html needed to display social plugins like Facebook like, google plus and twitter. At firsti defined an abstract class SocialButtonAbstract that is the base class that will be extended by the various plugins
abstract class SocialButtonAbstract {
protected $settings;
function __construct(array $settings = null) {
$this->_mergeSettings($settings);
}
protected function _mergeSettings(array $settings){
if ($settings !== null){
$this->settings = array_merge($this->settings, $settings);
}
}
abstract public function renderButton(array $settings = NULL);
abstract public function renderScript();
}
This is an example of FacebookLike (i post this example because it's central to my question)
require_once 'SocialButtonAbstract.php';
Class FacebookLike extends SocialButtonAbstract{
const FB_SEND = 'fb:send';
const FB_HREF = 'fb:href';
const FB_WIDTH = 'fb:width';
const FB_LAYOUT = 'fb:layout';
const FB_SHOW_FACES = 'fb:show-faces';
const FB_ACTION = 'fb:action';
const FB_FONT = 'fb:font';
const FB_COLORSCHEME = 'fb:colorscheme';
const FB_REF = 'fb:ref';
private $_apyKey = null;
protected $settings = array(
self::FB_SEND => 'true',//Display button SEND
self::FB_HREF => FALSE,//the URL to like. If FALSE defaults to the current page.
self::FB_WIDTH => '88',// the width of the Like button.
self::FB_LAYOUT => "button_count", //there are three options standard, button_count and box_count
self::FB_SHOW_FACES => 'false', //specifies whether to display profile photos below the button (standard layout only)
self::FB_ACTION => 'like',//the verb to display on the button. Options: 'like', 'recommend',
self::FB_FONT => FALSE,//the font to display in the button. Options: 'arial', 'lucida grande', 'segoe ui', 'tahoma', 'trebuchet ms', 'verdana'
self::FB_COLORSCHEME => 'light',// the color scheme for the like button. Options: 'light', 'dark'
self::FB_REF => FALSE// I don't understend how this works, for now is a Todo
);
public function setApiKey($apyKey){
$this->_apyKey = $apyKey;
}
public function renderScript(){
$apiKey = $this->_apyKey;
if ($apiKey !== null){
$apiKey = "&appId=$apiKey";
}else{
$apiKey = '';
}
$markup = <<<HTML
<div id="fb-root"></div>
<script>
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/all.js#xfbml=1$apiKey";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>
HTML;
return $markup;
}
public function renderButton(array $settings = null){
$this->_mergeSettings($settings);
$dataAttributes = array();
foreach ($this->settings as $attr => $value){
//Skip the values thatare FALSE, no need to render anything for them
if($value === FALSE){
continue;
}
//convert the keys from the format fb:send to the HTML5 data-send
$attr = str_replace("fb:", "data-", $attr);
$dataAttributes[$attr] = $value;
}
$markup = "<div class=\"fb-like\" ";
foreach ($dataAttributes as $dataAttr => $dataValue){
$markup .= "$dataAttr=\"$dataValue\" ";
}
$markup .= "></div>";
return $markup;
}
}
To display the various plugins i created a little helper class that provides the markup for the elements that surround the social buttons
class SocialPluginsHelper {
const ID_OF_WRAPPER = 'id_of_wrapper';
const CLASS_OF_WRAPPERS = 'class_of_wrappers';
const HTML_BEFORE_BUTTONS = 'html_before_buttons';
const HTML_AFTER_BUTTONS = 'html_after_buttons';
const CUSTOM_PLUGIN_STYLE = 'custom_plugin_style';
const TAG_OF_WRAPPER = 'tag_of_wrapper';
const STYLE_OF_WRAPPER = 'style_of_wrapper';
function __construct(array $settings = null) {
if ($settings !== null){
$this->settings = array_merge($this->settings, $settings);
}
}
private function createStyleAttribute($style){
$customStyle = '';
if(!empty($style)){
$customStyle = "style=\"$style\"";
}
return $customStyle;
}
private $plugins = array();
private $settings = array(
self::ID_OF_WRAPPER => "socialButtons",//id of the wrapper element
self::TAG_OF_WRAPPER => "div",//what HTML element to use as a wrapper, either DIV or UL (if you choose DIV, alle the children will be DIV too otherwise the will be LI)
self::CLASS_OF_WRAPPERS => "social",//Class of the children element
self::HTML_BEFORE_BUTTONS => '',//custom HTML before the plugins
self::HTML_AFTER_BUTTONS => '',//custom HTML after the plugins
self::STYLE_OF_WRAPPER => '',//custom style of the main wrapping element
self::CUSTOM_PLUGIN_STYLE => array()//this is some custom style thatwill be added to the elements that surround the plugins, useful to add extra width or inline options. It's //an associative array with the keys that equals the name of the classes (for example to add style to the Facebook plugin, use the key FacebookLike
);
public function add(SocialButtonAbstract $plugin){
$this->plugins [] = $plugin;
}
public function renderAllButtons(array $pluginSettings = null){
$settings = $this->settings;
$tagOfChildren = $settings[self::TAG_OF_WRAPPER] === "div" ? "div" : "li";
$tagOfWrapper = $settings[self::TAG_OF_WRAPPER];
$idOfWrapper = $settings[self::ID_OF_WRAPPER];
$htmlBeforeButtons = $settings[self::HTML_BEFORE_BUTTONS];
$classOfWrappers = $settings[self::CLASS_OF_WRAPPERS];
$htmlAfterButtons = $settings[self::HTML_AFTER_BUTTONS];
$customStyleOfWrapper = $this->createStyleAttribute($settings[self::STYLE_OF_WRAPPER]);
$markup = "<$tagOfWrapper $customStyleOfWrapper id=\"$idOfWrapper\">\n";
$markup .= "$htmlBeforeButtons\n";
foreach($this->plugins as $plugin){
$customSettings = isset($pluginSettings[get_class($plugin)]) && is_array($pluginSettings[get_class($plugin)]) ? $pluginSettings[get_class($plugin)] : array();
$customStyle = isset($settings[self::CUSTOM_PLUGIN_STYLE][get_class($plugin)]) ? $this->createStyleAttribute($settings[self::CUSTOM_PLUGIN_STYLE][get_class($plugin)]) : '';
$markup .= "<$tagOfChildren class=\"$classOfWrappers\" $customStyle>".$plugin->renderButton($customSettings)."</$tagOfChildren>\n";
}
$markup .= "$htmlAfterButtons\n</$tagOfWrapper>\n";
echo $markup;
}
public function renderAllScripts(){
foreach($this->plugins as $plugin){
echo $plugin->renderScript();
}
}
}
I also create a factory class to create instances of the plugins but it's not relevant to my question. I use the plugin like this:
//Initialize the helper with some custom markup options
$socialPlugins = new SocialPluginsHelper(array(
SocialPluginsHelper::HTML_AFTER_BUTTONS => '<div class="clear"></div>',
SocialPluginsHelper::HTML_BEFORE_BUTTONS => '<div class="clear"></div>',
SocialPluginsHelper::STYLE_OF_WRAPPER => 'margin-top:20px;',
SocialPluginsHelper::CUSTOM_PLUGIN_STYLE => array(
"TwitterButton" => "width:69px;",
"FacebookLike" => "width:88px;")
)
);
//Add the facebook plugin with some custom option
$socialPlugins->add(PluginFactory::create("FacebookLike", array(FacebookLike::FB_SEND => "false")));
//Add the Twitter plugin with no custom option
$socialPlugins->add(PluginFactory::create("TwitterButton"));
//Add the Gplus plugin with no custom option
$socialPlugins->add(PluginFactory::create("Gplus"));
Now here comes the part i'm less convinced of, if i have multiple Facebook Like button on the page i need to pass in the href to like for each plugin. for this reason, i do something like
$socialPlugins->renderAllButtons(array("FacebookLike" => array(FacebookLike::FB_HREF => $url)));
I used as a convention that i pass an array that has the name of the classes as "keys" amd an array of $settings as values.
Whati'm askin is
- in general, do you think that my class design is correct?
- is there some design pattern i should have implemented?
- am i doing the right thing by overriding the $settings each time or should i create a new instance of the plugin every time?
Thanks for your time