A little confused about MVC and where to put a database query - joomla

OK, so my Joomla app is in MVC format. I am still a little confused about where to put certain operations, in the Controller or in the Model. This function below is in the controller, it gets called when &task=remove. Should the database stuff be in the Model? It does not seem to fit there because I have two models editapp (display a single application) and allapps (display all the applications), now which one would I put the delete operation in?
/**
* Delete an application
*/
function remove() {
global $mainframe;
$cid = JRequest::getVar( 'cid', array(), '', 'array' );
$db =& JFactory::getDBO();
//if there are items to delete
if(count($cid)){
$cids = implode( ',', $cid );
$query = "DELETE FROM #__myapp_apps WHERE id IN ( $cids )";
$db->setQuery( $query );
if (!$db->query()){
echo "<script> alert('".$db->getErrorMsg()."');window.history.go(-1); </script>\n";
}
}
$mainframe->redirect( 'index.php?option=' . $option . '&c=apps');
}
I am also confused about how the flow works. For example, there is a display() function in the controller that gets called by default. If I pass a task, does the display() function still run or does it go directly to the function name passed by $task?

I would try to keep all database functionality in your model. If you don't know which model a method should go in, it's possible that you need to change your models to better reflect your problem.
In your case, though, I think this method would go in allapps since it can handle operations on multiple apps.
If you pass in a task, that method will be called. If you want to then call the display method, just call it at the end of your edit method.
When in doubt, take a look at the weblinks component's models and controllers. They are very simple and a good example of how to do MVC in Joomla!.

Related

MVC URL issues (Codeigniter)

I'm building a website using Codeigniter and I really like how in the MVC pattern URLs are used to reference controller methods. It seems very logical and intuitive however, I seem to be running in an array of issues with this very pattern!
So I am building an events website and currently I'm passing everything through one main Site controller, passing a number of parameters:
public function index($page = NULL, $city = NULL, $type_venue = NULL, $slug = NULL)
{
// if the page argument is empty show the homepage
if( ! ($page))
{
$page = 'home';
}
// create an array for passing to the views
$data = array(
'title_city' => $city,
'title_type_venue' => str_replace('-', ' ', $type_venue),
'locations' => $this->locations_model->load(),
'events' => $this->events_model->load($city, $type_venue, $slug),
'venues' => $this->venues_model->load($city, $slug)
);
// construct the page layout with the following views
$this->load->view('partials/head', $data);
$this->load->view('partials/header', $data);
$this->load->view('content/'.$page, $data);
$this->load->view('partials/footer');
}
This works fine, in that it loads content for the following URLs:
site.com/events/bristol/open-mic/city-varieties/another-incredible-event
site.com/events/bristol/open-mic/city-varieties/
site.com/events/bristol/open-mic/
site.com/events/bristol/
However if I want to pass anything else through this controller that isn't an event, i.e. register/user, I have to write a specific route for this!
Worth noting my routing is:
$route['(:any)'] = 'site/index/$1';
I could write separate controllers for each entity, i.e. events, venues, cities but each one would look largely like the above (correct?) in that each would need the parameters to get the data.
My question is - what is the best practice approach for developing long query strings like this? Is a single controller correct? It doesn't feel like it, but then multiple controllers would violate DRY, just because they all need so much similar data. Any help appreciated!
Avoid putting everything into a single controller; even further, in each controller, avoid putting everything into a single index function.
There is no need to write specific controllers for each function in Codeigniter - suggest you read that part again in the manual. Most of your routing will be done automatically for you if you follow the normal guidelines.
The more you try to use a single controller or function, the more you will have to add untestable, unmanageable, unscalable conditional code later.

How to override save() method in a component

Where and how I am overriding the save method in Joomla 3.0 custom component ?
Current situation:
Custom administrator component.
I have a list view that displays all people stored in table.
Clicking on one entry I get to the detailed view where a form is loaded and it's fields can be edited.
On save, the values are stored in the database. This all works fine.However, ....
When hitting save I wish to modify a field before storing it into the database. How do I override the save function and where? I have been searching this forum and googled quiet a bit to find ways to implement this. Anyone who give me a simple example or point me into the right direction ?
Thanks.
Just adding this for anyone who wants to know the answer to the question itself - this works if you explicitly wish to override the save function. However, look at the actual solution of how to manipulate values!
You override it in the controller, like this:
/**
* save a record (and redirect to main page)
* #return void
*/
function save()
{
$model = $this->getModel('hello');
if ($model->store()) {
$msg = JText::_( 'Greeting Saved!' );
} else {
$msg = JText::_( 'Error Saving Greeting' );
}
// Check the table in so it can be edited.... we are done with it anyway
$link = 'index.php?option=com_hello';
$this->setRedirect($link, $msg);
}
More details here: Joomla Docs - Adding Backend Actions
The prepareTable in the model (as mentioned above) is intended for that (prepare and sanitise the table prior to saving). In case you want to us the ID, though, you should consider using the postSaveHook in the controller:
protected function postSaveHook($model, $validData) {
$item = $model->getItem();
$itemid = $item->get('id');
}
The postSaveHook is called after save is done, thus allowing for newly inserted ID's to be used.
You can use the prepareTable function in the model file (administrator/components/yourComponent/models/yourComponent.php)
protected function prepareTable($table)
{
$table->fieldname = newvalue;
}

Custom Magento Module

So im creating a module in the backend, I have a shell module created (items in admin top menu and a page to visit.) basically I want to have an input field that the admin can type a number into then click a button "add", this will insert a row into an existing table in the database.
$connection = Mage::getSingleton('core/resource')->getConnection('core_write');
$connection->beginTransaction();
$fields = array();
$fields['name']= 'andy';
$connection->insert('test', $fields);
$connection->commit();
I have a table called "test" within my database. If I put the above code into my Controller file, it successfully adds a row to the database when i visit the admin page. But i need to allow the user to input the data that is inserted.
Would I have to move that code into the Model and somehow send the input data to the Model and let that do the work? or not. If this is correct could someone point me to a good place to research sending data to models? (if thats even possible)
iv tried lots of tutorials but they are all way to big for what I need, I dont need to display anything, I just need to have an input box and a save button.
EDIT
i have created a file block/Adminhtml/Form/Edit/Form.php which contains the following . . .
class AndyBram_Presbo_Block_Adminhtml_Form_Edit_Form extends Mage_Adminhtml_Block_Widget_Form
{
protected function _prepareForm()
{
$form = new Varien_Data_Form(
array(
'id' => 'edit_form',
'action' => $this->getUrl('*/*/test'),
'method' => 'post',
)
);
$form->setUseContainer(true);
$this->setForm($form);
$fieldset = $form->addFieldset('display', array(
'legend' => 'Display Settings',
'class' => 'fieldset-wide'
));
$fieldset->addField('label', 'text', array(
'name' => 'label',
'label' => 'Label',
));
if (Mage::registry('andybram_presbo')) {
$form->setValues(Mage::registry('andybram_presbo')->getData());
}
return parent::_prepareForm();
}
}
then in my controller i have 2 functions like below . . .
public function indexAction()
{
$this->loadLayout();
$this->_addContent($this->getLayout()->createBlock('presbo/adminhtml_form_edit_form'));
}
public function testAction()
{
echo 'form data here';
$this->loadLayout();
$this->renderLayout();
}
the form is displayed successfully but there is no button to send or say 'do an action'
Further Edit
i have successfully added a submit button to the form that successfully goes to the testAction and echo' "form data here".
how do i then access the data,
iv added the below line
$postData = $this->getRequest()->getPost();
now if i echo $postData, it just puts "array"
if i echo $postData[0] it doesnt put anything just a blank page
any ideas or pointers?
Magento is built as an MVC framework, thus you're right - you need to pass data from controller to the model, and do not do any DB updates directly in a controller's code. The best source for an example is the own Magento code - you can take any controller action, which saves data to DB to see, how it is done. E.g. check app/code/core/Mage/Adminhtml/controllers/NotificationController.php method markAsReadAction().
There you can see, that:
Data is retrieved from the request by calling
$this->getRequest()->getParam('id') - actually this is the answer
to the question, how to get the submitted data
Data is set to model, and then saved to the DB via call to the
$model->setIsRead(1)->save()
It is strongly encouraged to follow the same approach of working with models. This makes codes much better and easier to support.
Note, that "M" letter of "MVC" architecture in Magento is represented by two layers: Models and Resource Models.
Models:
Contain business logic of an entity. E.g. adding ten items to a
Shopping Cart model triggers a discount rule
Represented by classes with a general name of <Your_Module>_Model_<Model_Name>
If need to work with DB, then extend Mage_Core_Model_Abstract and have a Resource
Model, which is responsible for DB communication
Do not need to have basic save/load methods to be implemented, as the ancestor
Mage_Core_Model_Abstract already has all that routines ready to use
Created via call to Mage::getModel('<your_module>/<model_name>')
Resource Models:
Serve as DB abstraction layer, thus save/load data from DB, perform
other DB queries
Extend Mage_Core_Model_Resource_Db_Abstract in order to communicate with DB
Represented by classes with a general name of
<Your_Module>_Model_Resource_<Model_Name>
Automatically created by a corresponding model, when it needs to communicate with DB
So, in a controller you care about creating Models only. Resource Models are created by a Model automatically.
And, according to everything said above, your controller should look like:
public function testAction()
{
$model = Mage::getModel('your_module/your_model');
$model->setName('andy');
$model->save();
}
You can download a fully working example of the thing you need here.
There can be several variations to the code provided, depending on your specific case. But it represents a general approach to implementing the thing you want.

Codeigniter: Calling a method from a view

Here's my model, what it does is pulls a particular post from a user. Each post may also have comments, which are stored in an array called comments. I have everything working and I'm able to display the post along w/ the comments. The issue is, each comment has a post_date that displays when the comment was made. I need to call the function that "converts" the date into something like "3 weeks ago." The method TimeAgo is located in my user_model.php page. The excerpt shows a variable called data that's actually for the post, the comment is embedded inside the array and I loop through that in my view.
So the question is, is there a better way of handling this or do I have to call the TimeAgo method from within the view page?
Note, I'm using mongodb but it shouldn't matter if it's mongodb or mysql. Same thing ...
user_model.php
$query = array("_id" => new MongoId($plan_id), "username" => $username);
$fields = array("plan_title", "comments", "post_date");
$data = $collection_plans->findOne($query, $fields);
$data['date'] = self::TimeAgo($data['post_date']->sec);
$data['username'] = $username;
return $data;
If my understanding is correct I'd put the TimeAgo method inside a library or helper, then (auto)load whenever necessary and process the date before passing it to the view from within the controller.
this would allow you to access that method from within another model if required, or indeed any other part of your CI app, rather than from just within user_model.php
You call the model method from the controller and put it into a variable. You then pass the var to the view.

Need help figuring out how to write my zend view helper

I'm fairly new to Zend Framework and MVC in general so I'm looking for some advice. We have a base controller class in which we have some methods to obtain some user information, account configurations, etc.
So I'm using some of those methods to write out code in various controllers actions, but now I want to avoid duplicating this code and further more I would like to take this code outside of the controller and in a view helper as it is mainly to output some JavaScript. So the code in the controller would look like this:
$obj= new SomeModel ( $this->_getModelConfig () );
$states = $obj->fetchByUser ( $this->user->getId() );
//Fair amount of logic here using this result to prepare some javascript that should be sent to the view...
The $this->_getModelConfig and $this->user->getId() are things that I could do in the controller, now my question is what is the best way to pass that information to the view helper once i move this code out of the controller ?
Should I just call these methods in the controller and store the results into the view and have the helper pick it up from there ?
Another option I was thinking of was to add some parameters to the helper and if the parameters are passed then I store them in properties of the helper and return, and when called without passing the parameters it performs the work. So it would look like this:
From controller:
$this->view->myHelper($this->user->getId(), $this->_getModelConfig());
From view:
<?= $this->myHelper(); %>
Helper:
class Zend_View_Helper_MyHelper extends Zend_View_Helper_Abstract
{
public $userId = '';
public $config = null;
public function myHelper ($userId = null, $config = null)
{
if ($userId) {
$this->userId = $userId;
$this->config = $config;
} else {
//do the work
$obj = new SomeModel($this->config);
$states = $obj->fetchByUser($this->userId);
//do the work here
}
return $this;
}
}
Any advice is welcomed!
Firstly the ASP style ending tag here at "$this->myHelper(); %>" is bad practice, with that said it is more advisable to keep the logic in the model and the controller just being used to call the model, get the results and spit that to the view for viewing.
what I would do is, if i simply want to pass a bunch of values to the view, i stuff them in an associative array and send them over.
anyway you should not be doing your ...
"//Fair amount of logic here using this result to prepare some javascript that should be sent to the view..."
part in the controller, I would advice you to make a new model that does that logic stuff for you, and you just call your model in the controller pass it what ever arguments that are needed and then spit the result of that to the view.
The best way is to get the data from your model throught your controller, and then pass to the view. But if you really need a custom helper to echo the view parts, we only will know if you say exactly what you're trying to do.
If you already have this logic in a helper, try to just pass the parameters in your view myhelper($this->params); ?>
You may want take a look at this approach too:
// In your view to put javascript in the header
// You can loop trought your data and then use it to generate the javascript.
<?php $this->headScript()->captureStart(); ?>
$().ready(function(){
$('#slideshow').cycle({
fx: 'fade',
speed: 1000,
timeout: 6500,
pager: '#nav'
});
});
<?php $this->headScript()->captureEnd() ?>

Resources