iCalendar library (critique new use cases for qCal library)

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

Alright... well that's good. I look forward to your critique. :)
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

Alright I need some design advice. I decided to go with arborint's advice to seperate out io / rendering / etc. into their own classes. This means to render a standard icalendar-formatted calendar, I do something like:

Code: Select all

$cal = qCal::create();
// do stuff with calendar .. snip ..
$renderer = new qCal_Renderer_icalendar;
$renderer->render($cal)
That seems inconvenient to me. I mean, that would be fine if you were using a custom renderer (for hCal or xCal or whatever else you want to render it as), but most of the time you'll just be using the standard icalendar renderer. It would be more convenient (IMO) if you could just rely on the calendar object's render() method to know how to render. So if you haven't told it to do any different, it would just render w/default renderer (icalendar)

Code: Select all

$cal = qCal::create();
// since it hasn't been told any different, it just renders w/default renderer (to icalendar format)
$cal->render();
If you need to render it with a "special" renderer, you'd do this:

Code: Select all

$cal = qCal::create();
$cal->setRenderer(new qCal_Renderer_hCal);
$cal->render();
Or maybe even...

Code: Select all

$cal = qCal::create();
$cal->render(new qCal_Renderer_hCal);
What do you guys think?
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

I lean a little toward the setRenderer method myelf, but that is because I tend to do things like that myself when I develop. That is just me, not right or wrong.
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

Any advantage of setRenderer over simply:

Code: Select all

$cal = qCal::create();
$cal->render(new qCal_Renderer_hCal);
Thanks Everah! :-D
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

Render is typically a final step. It might not make much sense, but what if you wanted to render something in one way, then immediately render it in another (like I said, not much sense, but bear with me).

Using something a la setRenderer would give you the ability to set a renderer without rendering. So you could feasibly change the rendering engine midstream, if you wanted to, with the need for actual output.

Does that make any sense?
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

The Ninja Space Goat wrote:Any advantage of setRenderer over simply:

Code: Select all

$cal = qCal::create();
$cal->render(new qCal_Renderer_hCal);
Thanks Everah! :-D
I would probably stick to this method, simply because it would be easier to test than having to test two seperate methods. They essentially are doing the exact same thing, but this does it in fewer steps, with a simpler interface to the object.
Using something a la setRenderer would give you the ability to set a renderer without rendering. So you could feasibly change the rendering engine midstream, if you wanted to, with the need for actual output.
If this is the case, I would probably have a setRender(), but you could easily accomplish this anyways with just a render method anyways.
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

Thanks guys, that's precisely what I did... and it worked out nicely :)
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

Alright, I'm having some trouble here again. I'm hoping somebody can just give me a push in the right direction. I've tried several things, but none seem flexible enough. I've got qCal_Component and qCal_Property. Components can have other components, as well as properties as children. My renderer needs to be able to traverse this tree of qCal_Components / qCal_Properties and render them based on logic provided by subclasses of qCal_Renderer. Does that make sense?

I have a composite pattern set up for the component / property tree and that is working out just fine. The issue comes in when I try and give flexibility to classes that extend qCal_Renderer (this way I could have qCal_Renderer_hCal, qCal_Renderer_xCal, etc.). I'm just having a difficult time figuring out where to put what logic. Here is what I've been trying:

Code: Select all

abstract class qCal_Renderer
{
    public function render(qCal_Component $component)
    {
        $ret = '';
        foreach ($component->getChildren() as $child)
        {
            if ($child instanceof qCal_Component) $ret .= $this->renderComponent($child);
            elseif ($child instanceof qCal_Property) $ret .= $this->renderProperty($child);
        }
    }
    abstract protected function renderComponent(qCal_Component $component);
    abstract protected function renderProperty(qCal_Property $component);
}

class qCal_Renderer_icalendar
{
    protected function renderComponent(qCal_Component $component)
    {
        foreach ($component->getChildren() as $child)
        {
            // render each child
        }
    }
    protected function renderProperty(qCal_Property $component)
    {
         // render property
    }
}
To render the data as standard icalendar format is dead easy, but I would like for it to also be easy to render the data in xhtml, xml, json, whatever. This just doesn't feel like it will provide that kind of flexibility, and also I end up looping through component's children twice here... which feels wrong. DRY?
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

The Ninja Space Goat wrote:Alright, I'm having some trouble here again. I'm hoping somebody can just give me a push in the right direction. I've tried several things, but none seem flexible enough. I've got qCal_Component and qCal_Property. Components can have other components, as well as properties as children. My renderer needs to be able to traverse this tree of qCal_Components / qCal_Properties and render them based on logic provided by subclasses of qCal_Renderer. Does that make sense?
I haven't had a chance to look and think about your code (hopefully I will get some time), but the above sounds wrong to me because components should render and properties should be essentially models for the components/views. Maybe I am not understanding your terminology.
(#10850)
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

The Ninja Space Goat wrote:Is there any interest in a library such as this?
I might be interested to use this in our current project on a later stage
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

Actually arborint, I think you are on exactly the right path. If / when you get a chance to look at the code, I'd love to hear your opinion. Thanks :)
User avatar
Kieran Huggins
DevNet Master
Posts: 3635
Joined: Wed Dec 06, 2006 4:14 pm
Location: Toronto, Canada
Contact:

Post by Kieran Huggins »

I might be missing something here (and probably am) but shouldn't rendering different formats be entirely handled by different view templates? That would be DRY.
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Post by Luke »

Nope, you're not missing anything. That makes complete sense (and I believe that's what arborint was getting at...) that's exactly that push I needed :)
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

This just doesn't feel like it will provide that kind of flexibility, and also I end up looping through component's children twice here... which feels wrong. DRY?
I believe component tree traversal should be embedded into the components themselves - that's what the composite pattern is about. And you could implement renderer as a visitor: something like this:

Code: Select all

<?php
interface qCal_Component_Visitor_Interface
{
    public function visitComponent(qCal_Component $component);
    public function visitProperty(qCal_Property $component);
}

class qCal_Renderer_icalendar implements qCal_Component_Visitor_Interface
{
    protected function visitComponent(qCal_Component $component)
    {
		// render only that pertaining to component itself
    }
    protected function visitProperty(qCal_Property $component)
    {
         // render property
    }
}

interface qCal_Component_Interface 
{
	public function traverse(qCal_Component_Visitor_Interface $visitor);
}

class qCal_Component implements qCal_Component_Interface 
{
	public function traverse(qCal_Component_Visitor_Interface $visitor) 
	{
		$visitor->visitComponent($this);	
		foreach ($this->getChildren() as $child) {
			$child->traverse($visitor);
		}
	}
}

class qCal_Property implements qCal_Component_Interface 
{
	public function traverse(qCal_Component_Visitor_Interface $visitor) 
	{
		$visitor->visitProperty($this);
	}
}
?>
Post Reply