I am just starting with Joomla component development and MVC in general. Im also not incredibly strong in PHP but I am trying to build a component that gets json and displays it as a jQuery accordion list.
I started by creating a PHP script that does just that and it works fine.
Now Im on the component part and the mvc is tripping me up. I adapted the hello world component in the docs tutorial found here: http://docs.joomla.org/Developing_a_Model-View-Controller_Component/3.1/Introduction
But when I install the component and go to my component I get:
Fatal error: Call to undefined function getProject() in /[path to joomla]/components/com_insightly/views/insightly/view.html.php on line 23
So my view cant use a function I defined in my model. I must be missing something because I thought the controller assigned the model to the view auto-magically.
Here is some source, (sorry im not indenting 8 spaces on each line manually)
site\models\insightly.php
// import Joomla modelitem library
jimport('joomla.application.component.modelitem');
class InsightlyModelInsightly extends JModelItem
{
public function getProject($user_id)
{
$project_url = 'https://api.insight.ly/v2/projects/'.$user_id;
$project = array();
$html_tag = null;
$html_class = null;
$html_id = null;
$curl = curl_init($project_url);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Authorization: Basic NotGonnaShareThatNowAmI')
);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$curl_response = curl_exec($curl);
if ($curl_response){
$decoded = json_decode($curl_response, true);
$i = 0;
foreach ($decoded as $key=>$value){
// Eliminates Empty Fields and Saves to Multi-Dimensional Array
if ($value){
$value = id_mask($value);
}
switch($key)
{
case "PROJECT_NAME":
$key = null;
$html_tag = "h1";
break;
case "STATUS":
$key = "Status";
$html_tag = "li";
break;
case "PROJECT_DETAILS":
$key = "Description";
$html_tag = "li";
break;
case "RESPONSIBLE_USER_ID":
$key = "Project Manager";
$html_tag = "li";
break;
case "CATEGORY_ID":
$key = "Category";
$html_tag = "li";
break;
default:
$value = null;
}
if ($value){
$field = [$key, $value, $html_tag, $html_class, $html_id];
$project[$i] = $field;
$i++;
}
}
return $project;
}
else {
die('An error occurred retrieving Projects. Additional info: ' . curl_error($curl));
}
curl_close($curl);
}
public function getActiveTasks($user_id){
$project_tasks = getTasks($user_id);
$tasks = loopAndFind($project_tasks, 'STATUS', 'IN PROGRESS');
$i = 0;
foreach ($tasks as $key=>$value){
$active_tasks[$i] = taskStyler($key, $value);
$i++;
}
return $active_tasks;
}
public function getCompletedTasks($user_id){
$project_tasks = getTasks($user_id);
$tasks = loopAndFind($project_tasks, 'STATUS', 'COMPLETED');
$i = 0;
foreach ($tasks as $key=>$value){
$completed_tasks[$i] = taskStyler($key, $value);
$i++;
}
return $completed_tasks;
}
function getTasks($user_id){
$task_url = 'https://api.insight.ly/v2/tasks';
$curl = curl_init($task_url);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Authorization: Basic NotGonnaShareThatNowAmI')
);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$curl_response = curl_exec($curl);
if ($curl_response){
$decoded = json_decode($curl_response, true);
$project_tasks = loopAndFind($decoded, 'PROJECT_ID', $user_id);
return $project_tasks;
}
else {
die('An Error occurred retrieving Tasks. Additional info: ' . curl_error($curl));
}
curl_close($curl);
}
//Search Tool for Multi-Dimensional Arrays
function loopAndFind($array, $index, $search){
$returnArray = array();
foreach($array as $k=>$v){
if($v[$index] == $search){
$returnArray[] = $v;
}
}
return $returnArray;
}
//Formats dates as d/m/Y and removes the time
function date_formatter($value){
date_default_timezone_set('America/New_York');
$date = date_create($value);
$value = date_format($date, "m/d/Y");
return $value;
}
//Returns array with ALLCAPS corrected and html tags
function taskStyler ($key, $value){
$html_tag = "li";
$html_class = null;
$html_id = null;
switch ($key)
{
case "Title":
$key = "Task";
$html_tag = "h3";
break;
case "DUE_DATE":
$key = "Due Date";
$value = date_formatter($value);
break;
case "COMPLETED_DATE_UTC":
$key = "Date Completed";
$value = date_formatter($value);
break;
case "DETAILS":
$key = "Details";
$value = strip_tags($value);
break;
case "STATUS":
$key = "Status";
break;
case "PERCENT_COMPLETE":
$key = "Percent Complete";
$value = $value . "%";
break;
case "START_DATE":
$key = "Start Date";
$value = date_formatter($value);
break;
case "RESPONSIBLE_USER_ID":
$key = "Assigned to";
$value = id_mask($value);
break;
default:
$value = null;
}
if ($value){
$task = [$key, $value, $html_tag, $html_class, $html_id];
}
else {
$task = null;
}
return $task;
}
}
site\views\insightly\view.html.php
// import Joomla view library
jimport('joomla.application.component.view');
/**
* HTML View class for the Insightly Component
*/
class InsightlyViewInsightly extends JViewLegacy
{
// Overwriting JView display method
function display($tpl = null)
{
$user =& JFactory::getUser();
$user_id = $user->get( 'id' );
$user_id= "765727"; //Set for Development
// Assign data to the view
$this->project = getProject($user_id);
$this->active_tasks = getActiveTasks($user_id);
$this->completed_tasks = getCompletedTasks($user_id);
parent::display($tpl);
}
}
controller.php (which is empty)
// import Joomla controller library
jimport('joomla.application.component.controller');
/**
* Insightly Component Controller
*/
class InsightlyController extends JControllerLegacy
{
}
site\insightly.php
<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
// import joomla controller library
jimport('joomla.application.component.controller');
// Get an instance of the controller prefixed by Insightly
$controller = JControllerLegacy::getInstance('Insightly');
// Perform the Request task
$input = JFactory::getApplication()->input;
$controller->execute($input->getCmd('task'));
// Redirect if set by the controller
$controller->redirect();
site\views\insightly\tmpl\default.php
<article id='project'>
<?php
$iterations = count($this->project);
for ($i = 0; $i <= $iterations; $i++) {
echo('<' . $this->project[2]);
if (isset($this->project[3])) {
echo('class ="' . $this->project[3]);
}
if (isset($this->project[4])) {
echo('class ="' . $this->project[4]);
}
echo('>');
echo($this->project[0]);
echo(' : ');
echo($this->project[1]);
echo('</' . $this->project[2] . '>');
}
?>
<div id='tasks'>
<h2>Active Tasks</h2>
<section class='tasks'>
<?php
$number_of_tasks = count($this->active_tasks);
for ($i = 0; $i <= $number_of_tasks; $i++) {
$task_iterations = count($this->active_tasks[$i]);
for ($i = 0; $i <= $task_iterations; $i++) {
echo('<' . $this->active_tasks[$i][2]);
if (isset($this->active_tasks[$i][3])) {
echo('class ="' . $this->active_tasks[$i][3]);
}
if (isset($this->active_tasks[$i][4])) {
echo('class ="' . $this->active_tasks[$i][4]);
}
echo('>');
echo($this->active_tasks[$i][0]);
echo(' : ');
echo($this->active_tasks[$i][1]);
echo('</' . $this->active_tasks[$i][2] . '>');
}
}
?>
</section>
<h2>Completed Tasks</h2>
<section class='tasks'>
<?php
$number_of_tasks = count($this->completed_tasks);
for ($i = 0; $i <= $number_of_tasks; $i++) {
$task_iterations = count($this->completed_tasks[$i]);
for ($i = 0; $i <= $task_iterations; $i++) {
echo('<' . $this->completed_tasks[$i][2]);
if (isset($this->completed_tasks[$i][3])) {
echo('class ="' . $this->completed_tasks[$i][3]);
}
if (isset($this->completed_tasks[$i][4])) {
echo('class ="' . $this->completed_tasks[$i][4]);
}
echo('>');
echo($this->completed_tasks[$i][0]);
echo(' : ');
echo($this->completed_tasks[$i][1]);
echo('</' . $this->completed_tasks[$i][2] . '>');
}
}
?>
</section>
</div>
</article>
<script src="http://code.jquery.com/jquery-1.10.2.js"></script>
<script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
<script>
$(function () {
$(".tasks").accordion({ collapsible: true, active: false });
});
</script>
Any insight on why I cant get to my models function? Also I welcome the pointing out of any other blaring mistakes and security flaws.
EDIT: the idMask function was removed in my post due to sensitive content. It is probably not the issue.
EDIT: I have the component loading now and have updated the code but I'm having trouble with the arrays. taskStyler
should return an array to be set to $completed_tasks[i]
.
completed_tasks[0][0]
is correct but completed_tasks[0][1]
contains an array composed of what should becompleted_tasks[0][1-12]
.
This is my current version of the relevant functions:
Calls getTasks
and compiles array of tasks that are in progress
public function getActiveTasks($user_id){
$project_tasks = $this->getTasks($user_id);
$tasks = $this->loopAndFind($project_tasks, 'STATUS', 'IN PROGRESS');
$i = 0;
foreach ($tasks as $key=>$value){
$active_tasks[$i] = $this->taskStyler($key, $value);
$i++;
}
return $active_tasks;
}
Calls getTasks
and compiles array of tasks that are completed
public function getCompletedTasks($user_id){
$project_tasks = $this->getTasks($user_id);
$tasks = $this->loopAndFind($project_tasks, 'STATUS', 'COMPLETED');
$i = 0;
foreach ($tasks as $key=>$value){
$completed_tasks[$i] = $this->taskStyler($key, $value);
$i++;
}
return $completed_tasks;
}
Curl session with API server that returns an array of tasks (known to work perfectly)
public function getTasks($user_id){
$task_url = 'https://api.insight.ly/v2/tasks';
$curl = curl_init($task_url);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Authorization: Basic Dontlookatmypassword')
);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$curl_response = curl_exec($curl);
if ($curl_response){
$decoded = json_decode($curl_response, true);
$project_tasks = $this->loopAndFind($decoded, 'PROJECT_ID', $user_id);
return $project_tasks;
}
else {
die('An Error occurred retrieving Tasks. Additional info: ' . curl_error($curl));
}
curl_close($curl);
}
Returns an array representing a datafield of a task with the keys and some values edited for readability, and html tags, classes and ids appended.
public function taskStyler ($key, $value){
$html_tag = "li";
$html_class = null;
$html_id = null;
switch ($key)
{
case "Title":
$key = "Task";
$html_tag = "h3";
break;
case "DUE_DATE":
$key = "Due Date";
$value = $this->dateFormatter($value);
break;
case "COMPLETED_DATE_UTC":
$key = "Date Completed";
$value = $this->dateFormatter($value);
break;
case "DETAILS":
$key = "Details";
$value = strip_tags($value);
break;
case "STATUS":
$key = "Status";
break;
case "PERCENT_COMPLETE":
$key = "Percent Complete";
$value = $value . "%";
break;
case "START_DATE":
$key = "Start Date";
$value = $this->dateFormatter($value);
break;
case "RESPONSIBLE_USER_ID":
$key = "Assigned to";
$value = $this->idMask($value);
break;
default:
$value = null;
}
if ($value) {
$task = [$key, $value, $html_tag, $html_class, $html_id];
}
else{ $task = null;
}
return $task;
}