Apologies in advance for the long post...
I've been tasked with building a User-generated content site with ExpressionEngine. I had a rough idea of how I was going to proceed but - as I have progressed - it feels like I'm having to "bend the rules" more and more to achieve my intentions, which usually means that I'm doing something wrong :( Any feedback would therefore be greatly appreciated.
I'll first start by describing the intended architecture: The site consists of an open membership system which allows front-end registration, login, logout and profile capabilities. There are then a number of channels such as "jobs", "events", "workshops" - each following a similar structure of listing/view page - to which members can contribute from the front end. Once they have submitted an entry to one of these channels, they should be able to Edit or Delete the entry at a later date, or mark it as closed (filled).
So far, I've been working along the following lines of implementation: I'm using Structure to manage the pages and Zoo Visitor to handle the membership system (chosen as there were a number of checkbox fields that I needed to add to the registration form). This side of things is all working fine.
When a new job is added, I intend to have it use the id and job title to form the URL string, so that if another job already exists with the same title I don't end up with things like "web-developer-2". I haven't actually done this yet, but I imagine that I can just populate a hidden field on the add form.
For any one of these given areas (eg. /jobs/ ), I intended to have the following URL structure:
/jobs/ jobs listing page
/jobs/<id>-<job-title>/ job single page
/jobs/add/ add job page
/jobs/edit/<id>-<job-title>/ edit job page
/jobs/delete/<id>-<job-title>/ delete job page
/jobs/success/ success page for add/edit/delete job
/jobs/filter/today/ jobs posted today
/jobs/filter/this-week/ jobs posted in last 7 days
/jobs/filter/2013/oct/ jobs posted in october 2013
... (previous 12 months)
/jobs/filter/2012/nov/ jobs posted in november 2012
/jobs/filter/archive/ (including those with a status of 'closed')
Obviously on top of all this there is pagination which takes the form of p10, p20 etc (10 to a page)
I've been using Freebie to ignore anything after the /jobs/ segment, and then I've been using the jobs/index template a kind of controller:
{!-- Archive pages --}
{if freebie_3 == "archive"}
{embed="jobs/archive"}
{!-- CRUD pages --}
{if:elseif freebie_3 == "add"}
{embed="jobs/add"}
{if:elseif freebie_3 == "edit"}
{embed="jobs/edit"}
{if:elseif freebie_3 == "delete"}
{embed="jobs/delete"}
{if:elseif freebie_3 == "success"}
{embed="jobs/success"}
{!-- Listing / Single page --}
{if:else}
{embed="jobs/view"}
In the view template, I've the got:
{exp:channel:entries channel="jobs" required_entry="yes" limit="1" url_title="{freebie_last}" dynamic="no"}
{if no_results}
{!-- jobs listing page --}
{/if}
{!-- jobs detail page --}
{/exp:channel:entries}
In the add template I've got:
{if logged_in}
{embed="jobs/_form"}
{if:else}
<p>Sorry, you must be logged in to use this feature.</p>
{/if}
In the edit and delete templates, I've got:
{if logged_in}
{exp:channel:entries channel="jobs" required_entry="yes" limit="1" url_title="{freebie_last}" dynamic="no"}
{if no_results}
{redirect="404"}
{/if}
{!-- Edit code here --}
{/exp:channel:entries}
{if:else}
<p>Sorry, you must be logged in to use this feature.</p>
{/if}
The add/edit/delete templates will use Channel:form (formerly Safecracker) for the allow the adding, editing and deleting of entries. (I haven't written the code which detects if the form has been submitted and actually does the edit/delete action yet).
I guess I'll need to wrap the latter two functions in...
{if author_id == logged_in_member_id}
...to make sure that people can only edit/delete their own entries.
So I guess my question is...
Is there a better method - module or otherwise - of handling CRUD functionality from the front end, other than that which I have described?
It had dawned on me that a better approach might be to write a custom module that interacts directly with the database (rather than using the channels at all) as this might keep the model/controller cleaner and more separated from the view; but I haven't done this before so I'm hesitant to go down this road as I'm sure it will throw up a whole new set of hurdles...
Any advice is greatly appreciated :)