Best Practice: Displaying client-side messages and errors

When dealing with ActiveRecords, user "error" handling is very simple. Your code can simply do something like:


$model->addError('attribure_name','You did something wrong here!');

Then in the view the error is outputted through:


<?php echo CHtml::errorSummary($model); ?>

However, what about sending messages (errors or otherwise) back to the user when they aren’t tied directly to a model? Is there a “message handler” component tied to the application that can be used similarly? Something like:


Yii::app()->messageHandler->addMessage('message_handle','Thanks for pushing that button. The action has now been completed')'

Then the message could be outputted to the user via similar fashion, like so:


<?php echo CHtml::errorSummary(Yii::app()->messageHandler); ?>

Maybe this sort of functionality exists, but I haven’t found it in the documentation yet. One can obviously just pass a message to the view directly via render(), but this seems slightly “sloppy”. Any thoughts or recommendations on this topic? Thanks in advance for any input.

What you’re probably looking for is the user ‘flash’ functionality.

Look in the manual for setFlash(), getFlash() and hasFlash().

Thanks, Onman! That is what I was looking for…or at least the beginning of what I need.

Maybe this wouldn’t be useful in all cases, but I am currently working on an admin-type system where users log in and have access to manage data and run reports, etc. In this context, I am frequently sending messages back to the user, sometimes in relation to a model-based form, but often in relation to other activities that they perform. What I now need to create – unless this is already out there as well – is a widget (similar to the CHtml::error method) that encapsulates this get/setFlash functionality that I would include in all of my views. The widget would then check to see if any flash messages have been set, then output them already html formatted, just like CHtml::error.

The main problem I see with creating such a widget is that the getFlash method requires that you know the key of the message. There is no "getAllFlash()" method that would return an array of messages. Therefore, the CWebUser class would first have to be extended to provide the desired functionality. Then, in each view where it exists, the flashMessage widget could see if any messages exist, then loop through them and output each one. It would also be nice if you could set a message "type" when using setFlash, like message, notice, warning, error. Then, depending on the type that was set for the message the widget would apply a different style (css class) to the message on output.

Anyone else feel that this would be useful? I’ll probably end up creating this for my own purposes, but if I’m not the only one who needs this sort of thing maybe I’ll try to package it up. Again, the main issue is the lack of a getAllFlash method in the CWebUser class. I can make the output widget as a stand-alone entity, but if we also have to extend the base CWebUSer class then its not quite as convenient.

Anyone feel free to chime in with thoughts, suggestions, alternatives, or constructive criticism. Thanks!

luoshiben you speak with my words - I totally miss such functionality (either full-sized widget for error/warning/notice handling or the least getAllFlash() + flash message types). Knowing the exact name/index of the message doesn’t make sense in the context of what we need as functionality. Such message naming accounts for a dedicated communication rather than a message type based arrays of user notices / errors / etc. Extending CWebUser class makes most sense to me (either in official release or private mod) because get/setFlash() methods utilize set/getState methods + switchable auto-deletion. Since the information we need stored is heavily related to the “user” notion I guess a pair of methods like


CWebUser->setCommonMessage('message',$type='notice');

CWebUser->getCommonMessages($type,$flush=true);

in conjunction with CHtml extension like


CHtml::commonMessageSummary();

which relays on these to display separate sections with different types of messages grouped, would be best suited for the job. Since we could do other stuff (like logging, emailing, etc…) with these messages I favor the separation of their management and presentation. If you have already come up with something and you wish to share it with the rest of the world I would be glad to have a look at it.

cheers

  • monyii

You can set anything as message. Example:


Yii::app()->user->setFlash('common', array('test' => 'example'));

You are free to modify CWebUser for the things you need. Maybe add method addCommonFlash() and also register a function that sets all gathered messages onEndRequest of CWebApplication.

Pardon me for the huge snippet but I was thinking of something more like this:




class CWebUserEx extends CWebUser

{

	/**

	 *

	 * @var array Allowed common message types.

	 * @see setCommonMessage

	 */

	protected $allowedMessageTypes = array( 'notice','warning','error' );


	/**

	 * Method for setting custom messages into user session.

	 * @param string $message The text of the message.

	 * @param string $type One of (notice|warning|error).

	 * @return boolean Returns true on success, false on failure.

	 * @todo add second optional parameter $params for eventual i18n.

	 */

	public function setCommonMessage($message, $type='notice')

	{


		if(in_array($type, $this->allowedMessageTypes))

		{

			if(!isset($this->getState('commonMessages'.$type)) || !is_array($this->getState('commonMessages'.$type)))

			{

				$this->setState('commonMessages'.$type, array());

			}

			$current_messages = $this->getState('commonMessages'.$type);

			$current_messages[] = $message;

			$this->setState('commonMessages'.$type, $current_messages);

		}

		else

		{

			$this->setCommonMessage('Wrong common message type: SET['.$type.']', 'warning');

		}


		return in_array($message, $this->getState('commonMessages'.$type));

	}


	/**

	 *

	 * @param string $type One of (notice|warning|error).

	 * @param boolean $flush Flag to remove the messages of certain type, after retrieval.

	 * @return array Returns array with possible messages, empty if there aren't any or wrong message type is passed.

	 */

	public function getCommonMessages($type, $flush=true)

	{

		if(in_array($type, $this->allowedMessageTypes))

		{

			$messages = isset ( $this->getState('commonMessages'.$type) ) ? $this->getState('commonMessages'.$type) : array();

			if($flush && isset ( $this->getState('commonMessages'.$type) ))

			{

				$this->setState('commonMessages'.$type, null);

			}

		}

		else

		{

			$messages = array('Wrong common message type: GET['.$type.']');

		}


		return $messages;

	}

}



Try this:




class CWebUserEx extends CWebUser

{

	/**

	 *

	 * @var array Allowed common message types.

	 * @see setCommonMessage

	 */

	protected $allowedMessageTypes = array( 'notice','warning','error' );


	/**

	 * Method for setting custom messages into user session.

	 * @param string $message The text of the message.

	 * @param string $type One of (notice|warning|error).

	 * @return boolean Returns true on success, false on failure.

	 * @todo add second optional parameter $params for eventual i18n.

	 */

	public function setCommonMessage($message, $type='notice')

	{

		$user=Yii::app()->user;

		if(in_array($type, $this->allowedMessageTypes))

		{

			$flash=$user->getFlash($type,array());

			$flash[]=$message;

			$user->setFlash($type,$flash);

			return true;

		}


		$user->setFlash('warning','Wrong common message type: SET['.$type.']');

		return false;

	}


	/**

	 *

	 * @param string $type One of (notice|warning|error).

	 * @param boolean $flush Flag to remove the messages of certain type, after retrieval.

	 * @return array Returns array with possible messages, empty if there aren't any or wrong message type is passed.

	 */

	public function getCommonMessages($type, $flush=true)

	{

		if(in_array($type, $this->allowedMessageTypes))

			$messages=Yii::app()->user->getFlash($type,array(),$flush);

		else

			$messages = array('Wrong common message type: GET['.$type.']');


		return $messages;

	}

}



[/quote]