I have this code in my controller (admin):
function save(){
$model = $this->getModel('mymodel');
if ($model->store($post)) {
$msg = JText::_( 'Yes!' );
} else {
$msg = JText::_( 'Error :(' );
}
$link = 'index.php?option=com_mycomponent&view=myview';
$this->setRedirect($link, $msg);
}
In model I have:
function store(){
$row =& $this->getTable();
$data = JRequest::get('post');
if(strlen($data['fl'])!=0){
return false;
}
[...]
And this is working - generate error message, but it return to items list view. I want to stay in edit view with entered data. How to do it?
In your controller you can:
if ($model->store($post)) {
$msg = JText::_( 'Yes!' );
} else {
// stores the data in your session
$app->setUserState('com_mycomponent.edit.mymodel.data', $validData);
// Redirect to the edit view
$msg = JText::_( 'Error :(' );
$this->setError('Save failed', $model->getError()));
$this->setMessage($this->getError(), 'error');
$this->setRedirect(JRoute::_('index.php?option=com_mycomponent&view=myview&id=XX'), false));
}
then, you will need to load the data from session with something like:
JFactory::getApplication()->getUserState('com_mycomponent.edit.mymodel.data', array());
normally this is loaded in the method "loadFormData" in your model. Where to load that data will depend on how are you implementing your component. If you are using the Joomla's form api then you can add the following method to your model.
protected function loadFormData()
{
// Check the session for previously entered form data.
$data = JFactory::getApplication()->getUserState('com_mycomponent.edit.mymodel.data', array());
if (empty($data)) {
$data = $this->getItem();
}
return $data;
}
EDIT:
BUT please note, that Joomla's API already can do all this for you if you controller inherits from "JControllerForm", you don't need to rewrite the save method. The best way to create your component is copying what is in Joomla's core components, com_content for example
It is not recommended to rewrite save or any method.
If you really want to override something and want to update something before or after save, you should use JTable file.
For Example:
/**
* Example table
*/
class HelloworldTableExample extends JTable
{
/**
* Method to store a node in the database table.
*
* #param boolean $updateNulls True to update fields even if they are null.
*
* #return boolean True on success.
*/
public function store($updateNulls = false)
{
// This change is before save
$this->name = str_replace(' ', '_', $this->name);
if (!parent::store($updateNulls))
{
return false;
}
// This function will be called after saving table
AnotherClass::functionIsCallingAfterSaving();
}
}
You can extends any method using JTable class and that's the recommended way to doing it.
Related
I am trying to pass $request from a function in controller to a function in model.
THis is my controller function:
PostController.php
public function store(Request $request, post $post)
{
$post->title = $request->title;
$post->description = $request->description;
$post->save();
return redirect(route('post.index'));
}
how save data in model Post.php?
I want the controller to only be in the role of sending information. Information is sent to the model. All calculations and storage are performed in the model
Thanks
You can make it even easier. Laravel has it's own helper "request()", which can be called anywhere in your code.
So, generally, you can do this:
PostController.php
public function store()
{
$post_model = new Post;
// for queries it's better to use transactions to handle errors
\DB::beginTransaction();
try {
$post_model->postStore();
\DB::commit(); // if there was no errors, your query will be executed
} catch (\Exception $e) {
\DB::rollback(); // either it won't execute any statements and rollback your database to previous state
abort(500);
}
// you don't need any if statements anymore. If you're here, it means all data has been saved successfully
return redirect(route('post.index'));
}
Post.php
public function postStore()
{
$request = request(); //save helper result to variable, so it can be reused
$this->title = $request->title;
$this->description = $request->description;
$this->save();
}
I'll show you full best practice example for update and create:
web.php
Route::post('store/post/{post?}', 'PostController#post')->name('post.store');
yourform.blade.php - can be used for update and create
<form action='{{ route('post.store', ['post' => $post->id ?? null]))'>
<!-- some inputs here -->
<!-- some inputs here -->
</form>
PostController.php
public function update(Post $post) {
// $post - if you sent null, in this variable will be 'new Post' result
// either laravel will try to find id you provided in your view, like Post::findOrFail(1). Of course, if it can't, it'll abort(404)
// then you can call your method postStore and it'll update or create for your new post.
// anyway, I'd recommend you to do next
\DB::beginTransaction();
try {
$post->fill(request()->all())->save();
\DB::commit();
} catch (\Exception $e) {
\DB::rollback();
abort(500);
}
return redirect(route('post.index'));
}
Based on description, not sure what you want exactly but assuming you want a clean controller and model . Here is one way
Model - Post
class Post {
$fillable = array(
'title', 'description'
);
}
PostController
class PostController extend Controller {
// store function normally don't get Casted Objects as `Post`
function store(\Request $request) {
$parameters = $request->all(); // get all your request data as an array
$post = \Post::create($parameters); // create method expect an array of fields mentioned in $fillable and returns a save dinstance
// OR
$post = new \Post();
$post->fill($parameters);
}
}
I hope it helps
You need to create new model simply by instantiating it:
$post = new Post; //Post is your model
then put content in record
$post->title = $request->title;
$post->description = $request->description;
and finally save it to db later:
$post->save();
To save all data in model using create method.You need to setup Mass Assignments when using create and set columns in fillable property in model.
protected $fillable = [ 'title', 'description' ];
and then call this with input
$post = Post::create([ 'parametername' => 'parametervalue' ]);
and if request has unwanted entries like token then us except on request before passing.
$post = Post::create([ $request->except(['_token']) ]);
Hope this helps.
I find to answer my question :
pass $request to my_method in model Post.php :
PostController.php:
public function store(Request $request)
{
$post_model = new Post;
$saved = $post_model->postStore($request);
//$saved = response of my_method in model
if($saved){
return redirect(route('post.index'));
}
}
and save data in the model :
Post.php
we can return instance or boolean to the controller .
I returned bool (save method response) to controller :
public function postStore($request)
{
$this->title = $request->title;
$this->description = $request->description;
$saved = $this->save();
//save method response bool
return $saved;
}
in this way, all calculations and storage are performed in the model (best way to save data in MVC)
public function store(Request $request)
{
$book = new Song();
$book->title = $request['title'];
$book->artist = $request['artist'];
$book->rating = $request['rating'];
$book->album_id = $request['album_id'];
$result= $book->save();
}
After I have updated a customer (which works fine)
public function update($customer_acc) {
if($this->session->userdata('logged_in'))
{
$id= $this->input->post('did');
$data = array(
'customer_name' => $this->input->post('dname')
);
$this->load->model('update_model');
$this->update_model->update_customer($id,$data);
}
else
{
//If no session, redirect to login page
redirect('login', 'refresh');
}
}
How can i then load back the view customer function. What i need is after the update has been completed so then go back to viewing the customer.
public function view($customer_acc) {
if($this->session->userdata('logged_in'))
{
$this->load->model('display_single_customer');
$customers = $this->display_single_customer->view_single_customer($customer_acc);
$data['customer_acc'] = $customers['customer_acc'];
$data['customer_name'] = $customers['customer_name'];
$this->load->view('customers_single_view', $data);
}
else
{
//If no session, redirect to login page
redirect('login', 'refresh');
}
}
you just exit to another method in your controller to show the new page. for passing the id you can either pass it directly
if ( some condition ) {
$id = $this->input->post('did', TRUE);
// blah blah blah
// success -- now go to show customer method
$this->showCustomer($id) ; }
function showCustomer($id){
// get the customer to display using the $id that was passed
$customers = $this->display_single_customer->view_single_customer($id);
OR you can declare the id with $this-> and then it is available to any method in the controller
$this->id = $this->input->post('did', TRUE);
// blah blah
$this->showCustomer() ; }
function showCustomer(){
// get the customer to display using $this->id
$customers = $this->display_single_customer->view_single_customer($this->id);
// etc etc
I think i may have sussed it I can use a redirect using the $id which is the customer account number
redirect("/customers/view/$id");
Is this the correct way, It works but is it best practice ?
I am trying to update the popularity count of Magento's Tag module by interacting with this core function in Mage_Tag_Model_API
public function update($tagId, $data, $store)
{
$data = $this->_prepareDataForUpdate($data);
$storeId = $this->_getStoreId($store);
/** #var $tag Mage_Tag_Model_Tag */
$tag = Mage::getModel('tag/tag')->setStoreId($storeId)->setAddBasePopularity()->load($tagId);
if (!$tag->getId()) {
$this->_fault('tag_not_exists');
}
// store should be set for 'base_popularity' to be saved in Mage_Tag_Model_Resource_Tag::_afterSave()
$tag->setStore($storeId);
if (isset($data['base_popularity'])) {
$tag->setBasePopularity($data['base_popularity']);
}
if (isset($data['name'])) {
$tag->setName(trim($data['name']));
}
if (isset($data['status'])) {
// validate tag status
if (!in_array($data['status'], array(
$tag->getApprovedStatus(), $tag->getPendingStatus(), $tag->getDisabledStatus()))) {
$this->_fault('invalid_data');
}
$tag->setStatus($data['status']);
}
try {
$tag->save();
} catch (Mage_Core_Exception $e) {
$this->_fault('save_error', $e->getMessage());
}
return true;
}
In my controller I have this :
public function clickAction()
{
$tagString = $this->getRequest()->getParam('tag');
$tagByName = Mage::getModel('tag/tag')->loadByName($tagString);
$tagId = $tagByName->getTagId();
$basePopularity = ['base_popularity' => '13']; // hard coding while testing
Mage::getModel('tag/api')->update($tagId, $basePopularity, 1);
}
If I put a log statement in this part of the update function :
try {
// log stuff
$tag->save();
}
I can see it makes it to that try but there is no change in the data. What did I screw up? Any other ideas on how I can update the popularity of a tag through a controller? Using this same method and adding 'name' => 'blah' to that $data array parameter works fine..
I also found in Mage_Tag_Model_Indexer_Summary.php this method defined in the PHPdoc * #method Mage_Tag_Model_Indexer_Summary setPopularity(int $value) Maybe that is what I need... can someone provide an example showing how I could use that magic setter?
Try adding Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID); at the start of your clickAction function. base_popularity can only be updated from admin store.
I would like a best practice for this kind of problem
I have items, categories and category_item table for a many to many relationship
I have 2 models with these validations rules
class Category extends Basemodel {
public static $rules = array(
'name' => 'required|min:2|max:255'
);
....
class Item extends BaseModel {
public static $rules = array(
'title' => 'required|min:5|max:255',
'content' => 'required'
);
....
class Basemodel extends Eloquent{
public static function validate($data){
return Validator::make($data, static::$rules);
}
}
I don't know how to validate these 2 sets of rules from only one form with category, title and content fields.
For the moment I just have a validation for the item but I don't know what's the best to do:
create a new set of rules in my controller -> but it seems redundant
sequentially validate Item then category -> but I don't know how to handle validations errors, do I have to merges them? and how?
a 3rd solution I'm unaware of
here is my ItemsController#store method
/**
* Store a newly created item in storage.
*
* #return Redirect
*/
public function store()
{
$validation= Item::validate(Input::all());
if($validation->passes()){
$new_recipe = new Item();
$new_recipe->title = Input::get('title');
$new_recipe->content = Input::get('content');
$new_recipe->creator_id = Auth::user()->id;
$new_recipe->save();
return Redirect::route('home')
->with('message','your item has been added');
}
else{
return Redirect::route('items.create')->withErrors($validation)->withInput();
}
}
I am very interested on some clue about this subject
thanks
One way, as you pointed yourself, is to validate it sequentially:
/**
* Store a newly created item in storage.
*
* #return Redirect
*/
public function store()
{
$itemValidation = Item::validate(Input::all());
$categoryValidation = Category::validate(Input::all());
if($itemValidation->passes() and $categoryValidation->passes()){
$new_recipe = new Item();
$new_recipe->title = Input::get('title');
$new_recipe->content = Input::get('content');
$new_recipe->creator_id = Auth::user()->id;
$new_recipe->save();
return Redirect::route('home')
->with('message','your item has been added');
}
else{
return Redirect::route('items.create')
->with('errors', array_merge_recursive(
$itemValidation->messages()->toArray(),
$categoryValidation->messages()->toArray()
)
)
->withInput();
}
}
The other way would be to create something like an Item Repository (domain) to orchestrate your items and categories (models) and use a Validation Service (that you'll need to create too) to validate your forms.
Chris Fidao book, Implementing Laravel, explains that wonderfully.
You can also use this:
$validationMessages =
array_merge_recursive(
$itemValidation->messages()->toArray(),
$categoryValidation->messages()->toArray());
return Redirect::back()->withErrors($validationMessages)->withInput();
and call it in the same way.
$validateUser = Validator::make(Input::all(), User::$rules);
$validateRole = Validator::make(Input::all(), Role::$rules);
if ($validateUser->fails() OR $validateRole->fails()) :
$validationMessages = array_merge_recursive($validateUser->messages()->toArray(), $validateRole->messages()->toArray());
return Redirect::back()->withErrors($validationMessages)->withInput();
endif;
I created a "callback" function to check if the username exists in the DB.
I have multiple rules for the "username" field, but the only thing that work is my callback function. It refuses to check against the other rules. I tried leaving the field empty, and the "required" rule never kicked in.
Controller:
account.php
function register() {
$this->load->library('validation');
$fields['username'] = "trim|required|callback_username_check";
etc ...
etc ...
$this->validation->set_rules($fields);
if ($this->validation->run()) {
$records = array();
$records['username'] = $this->validation->username;
etc ...
etc ...
$data = $this->account_model->registerNewAccount($records);
}
$this->load->view('register_view');
}
function username_check($username) {
$m = new Mongo();
$collection = $m->selectDB( DBNAME )->selectCollection( TABLE );
$data = $collection->count(array("username" => $username) );
if($data == 1) {
$this->validation->set_message('username_check', '%s is already taken!');
return false;
} else {
return true;
}
}
Try using the new form_validation class here:
http://ellislab.com/codeigniter/user_guide/libraries/form_validation.html
I believe there was a bug about it.