How to call a function of component from view? - php

Q1 : How to call a function of component from view?
one of my function is using most of the controllers.
public function actionDynamicdepartment()
{
//Department
$data = Department::model()->findAll('p_id=0 AND company_id=:company_id', array(':company_id'=>(int) $_POST['company_id']));
$data = CHtml::listData($data,'id','name');
$dddepatment = "<option value=''>Please select a department</option>";
foreach($data as $value=>$name)
$dddepatment .= CHtml::tag('option', array('value'=>$value),CHtml::encode($name),true);
//Section and Team
$ddsection = "<option value=''>Please select a section</option>";
$ddteam = "<option value=''>Please select a team</option>";
// return data (JSON formatted)
echo CJSON::encode(array(
'dddepartment'=>$dddepatment,
'ddsection'=>$ddsection,
'ddteam'=>$ddteam
));
}
I want to put it into component or some place.
And I want to call those function from my views. e.g
<div class="row">
<?php echo $form->labelEx($model,'company_id'); ?>
<?php
$records = Company::model()->findAll();
$company = CHtml::listData($records, 'id', 'name');
echo $form->dropDownList($model,'company_id', $company, array('prompt'=>'Please select a company',
'ajax' => array(
'type'=>'POST', //request type
'url'=>CController::createUrl('department/dynamicdepartment'), //url to call.
'dataType'=>'json',
'data'=>array('company_id'=>'js:this.value'),
'success'=>'function(data) {
$("#FaMovehistory_department_id").html(data.dddepartment);
$("#FaMovehistory_section_id").html(data.ddsection);
$("#FaMovehistory_team_id").html(data.ddteam);
}',
)
)
);
?>
</div>
Or
Q2 : put those function at one of the controller (department.php). And can I call those function from different view?
Q3 : if do as Q2, is there any traffic?

What I use to do is to define a CWidget (like Dmitry said) and then I create some functions (I tend to make them static, as if it was a library), so if, for instance, your Widget is called "Departments", you could do something like this:
Yii::import("application.components.Departments");
Departments::actionDynamicdepartment();
Pretty straightforward. You could, for this situation, return that CJson instead of echoing. However, you may not be interested in having a static response from this method.
For your last questions, I tend to approach the population of dropdowns in a more classic manner, having an ajax call (I use jquery) requesting a central controller and passing some variables to it. That, of course, generates traffic.
So, to sum up, if you want to recieve a list of departments and avoid changing it during the current page, you could go for a widget/component. If, on the other side, your dropdown needs to be responsive along with the rest of the items in a form, a controller's action is your best (and probably unique) option.

You need to create a widget instead of component.
Each widget has its own view, and you will be able to describe in its class logic of his behaviour (move the code from the controller) Then call it in the main view:
<?php $this->widget('path.to.your.widget') ?>
Read more: http://www.yiiframework.com/doc/api/1.1/CWidget and http://www.yiiframework.com/doc/guide/1.1/en/basics.view#widget

Related

Joomla custom fields inside template

I would like to customize my template for Joomla 3.7 so that I can use the new feature of Joomla 3.7, Custom fields (com_fields), and display and format them via CSS in my template where I need to display them.
Can someone suggest me the PHP code I should use in the template to display field(s), some example please.
Thanks in advance.
For everyone getting late to the party. In case you want to use your custom form fields in a Module-Override (which really are the only way to modify j!-templates, so google 'joomla template override') you can use this handy snippet:
<?php
JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php');
$jcFields = FieldsHelper::getFields('com_content.article', $item, true);
$itemCustomFields = array();
foreach($jcFields as $field) {
$itemCustomFields[$field->name] = $field->rawvalue;
}
?>
Now you cna use your customfields like so: itemCustomFields['customFieldName1']
Haven't tested in article overrides. May soon, if so, this will get updated.
certainly not the right way to do it but I had the same need and I found a work around based on https://www.giudansky.com/news/12-coding/146-joomla-custom-fields
Copie default.php from /components/com_content/views/article/tmpl/default.php to
templates/YOUR_THEME/html/com_content/article/default.php
Add following code line 25 :
$myCustomFields = array();
foreach($this->item->jcfields as $field) {
$myCustomFields[$field->name] = $field->value;
}
$GLOBALS['myCustomFields'] = $myCustomFields;
Typically you put on a global var the content of fields attached to your article.
On your template page you can know retrieved value of your field.
just print_r($GLOBALS['myCustomFields']); to view the content of your array.
That will do the trick waiting for a better answer..
This is absolutely the wrong way to do this I think but I was tearing my hair out so i came up with this quick db query to return custom field values in the template. surely this violates some kind of joomla protocol?
obviously this assumes you can get $articleid into your template already which is the Current ID of your article.
I too am waiting on a better solution but hope this helps
$db =& JFactory::getDBO();
$sql = "select * from #__fields_values where `item_id` = $articleid";
$db->setQuery($sql);
$fieldslist = $db->loadObjectList();
echo $fieldslist[0]->value;
echo $fieldslist[1]->value;
echo $fieldslist[your field ID here]->value;
I found it was easiest to follow how com_fields does it in its rendering code. In Joomla!3.7+, you'll find it in [joomla_root]/components/com_fields/layouts/fields/render.php .
Here are the main parts you need to reproduce the formatting that Joomla has:
JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php');
<dl class="fields-container">
<?php foreach ($this->item->jcfields as $field) : ?>
<?php // If the value is empty do nothing ?>
<?php if (!isset($field->value) || $field->value == '') : ?>
<?php continue; ?>
<?php endif; ?>
<?php $class = $field->params->get('render_class'); ?>
<dd class="field-entry <?php echo $class; ?>">
<?php echo FieldsHelper::render($context, 'field.render', array('field' => $field)); ?>
</dd>
<?php endforeach; ?>
</dl>
This loops through all available tags for the component or article. The nice thing about this method is it still applies the render classes you include with the fields.
Make sure to set Automatic Display to Do not automatically display on your fields; otherwise you will see them twice on your page view.
If you want to just target specific fields to show, you can use the name of the field to target it. (The label and value pair is underneath.) See the field Joomla docs for more info.
I implemented this small function to get specific custom field values:
function getCustomFieldValue($field_name, $article_id, $default_value = '') {
// Load custom field list
$fields = FieldsHelper::getFields('com_content.article', $article_id, true);
$field_ids = array_column($fields, 'id', 'name');
$model = JModelLegacy::getInstance('Field', 'FieldsModel', array('ignore_request' => true));
// Return the value if the field exists, otherwise the default
return array_key_exists($field_name, $field_ids)
? $model->getFieldValue($field_ids[$field_name] , $article_id)
: $default_value;
}
Usage:
$some_field_value = getCustomFieldValue('some-field-name', $some_article_id);
Optimization: I placed the function into a helper class, implemented the variables $fields, $field_ids and $model static and checked if they are already loaded to prevent redundant loading of the same data.

Use ajax to fill cities in a dropdown in a mvc based php application?

In a PHP MVC application, the model part has a city.php file that defines a city class.
The city class has a method to fetch all cities for a province i.e.
getCitiesByProvince('ProvinceId')
When the user selects a province from a province dropdown, I need to make an ajax call to getCitiesByProvince('ProvinceId') depending upon what value for Province Id was selected by the user inside the Province dropdown.
How do I do this?
Edit: 1) The page uses the GET method and not the POST method. 2) The controller file uses a switch($action) to execute a specific case. For example to fetch all cities
case 'cities': //Get all cities for a provinceId
$city= new city();
$cities = $City->getCitiesByProvince('ProvinceId');
echo json_encode($cities);
Action is probably a hidden html attribute that tells the php script what action/case to perform... maybe something like
<input type="hidden" name="action" value="cities" />
This is another developer's code that i'm not too familiar with at the moment and i'm just trying to guess how things work by looking into various files...
Have your PHP file do the following:
<?php
$province = $_GET['p'];
$cities = array();
$city_results = ... // fetch cities from database
foreach($city_results as $city_result) {
$cities[] = $city_result->city;
}
echo json_encode($cities);
?>
Have your Javascript do the following:
$.ajax({
type: "GET",
url: "php_url_path",
data: { p: get_province_here() },
success: function (cities) {
for (var i = 0; i < cities.length; i++) {
... // handle options here
}
}
});
This will create the inter-file connection that you seek. Enjoy and good luck!
You wouldn't be able to call this straight from the model insted you'd need to make an action in the controller called getcities or whatever and use $.ajax or $.get or $.post or $.getJSON to retreve the results assuming they'd be in json format heres an example using $.post
if your controller is citycntrl.php inside the controller you would do
//whatever logic to get an array of cities in whatever format you'd like
ex: '{zip}' => '{cityname}', or just {cityname} with regular keys
using the $_POST['providenceId']; key were sending with jQuery
What you'd want to do is run the array through json_encode($arrayname); and echo that out in your controller based on the providenceId post value.
Heres a better code example for your drop down senario i misread the first time and missed the drop down part
$.post('controller/getcities', {
provinceId: $('#{dropdownIDorCLASS}:selected').val();
}, function (data) {
//data will contain your list of cities passed down from your controller.
//do whatever with returned json data
}, 'json');
if I'm just confusing the crap out of you feel free to contact me on Skype and i hopefully assist a bit better UN: fallen.networks

Multiple _form views on a single create view [Yii]

I'm trying to merge 3 models to create a fourth one. I have model1, model2 and model3 and I want to merge them into modelMaster. I've also created controllers for all of them. When I call modelMaster/create action, I render the modelMaster/create view which renders the modelMaster/_form view. Inside this _form view, I also want to render model1/_form, model2/_form and a CHtml::dropDownList(), wich takes datas from model3. However, this doesn't work. How can I combine these three different views into one another?
If you try to skip the form generate from the _form views and use unique model names, I think you can use this manual: single form with more models
So the generate of the form definition handles always the parent view and the _form's only the inputs
The other way to use single model in views, create a form model by extend CFormModel, and handle the data binding between this model and the wrapped submodels
If you want to nest several forms into one master form you have to adjust the form templates accordingly. All of your modelMaster/create, model1/_form, model2/_form-views create and render a new CActiveForm (and thus several <form> tags).
Since you cannot nest form-elements in html (how should html know which action to pass the data to) you have to avoid this by doing the following:
Extract the inputs of the form you want to nest into a new view, e.g. model1/_formInputs would look like
...
<?php echo $form->labelEx($model,'name'); ?>
<?php echo $form->textField($model,'name');
<?php echo $form->error($model,'name');
...
alter the model1/create and the other views and get a reference to the form created there, by assigning the return of $this->beginWidget to the variable $form (if not already done):
<?php $form = $this->beginWidget('CActiveForm', array(
'id'=>'foo',
)); ?>
replace the former input fields with
<?php $this->renderPartial('model1/_formInputs', array('form' => $form); ?>
Now, for example the old model1/create-view should work as expected
To get your multi-model-form working you just have to get the reference to the form created in modelMaster/create and use it to renderPartial all */_formInputs you require. Please also remember to include the models for the inputs into the renderPartial-call. So modelMaster/create would look something like:
<?php $form = $this->beginWidget('CActiveForm', array(
'id'=>'foo',
)); ?>
/* Master Inputs here */
// Rendering other models' inputs
<?php $this->renderPartial('model1/_formInputs', array('form' => $form, 'model' => $model1); ?>
<?php $this->renderPartial('model2/_formInputs', array('form' => $form, 'model' => $model2); ?>
/* Render Form Buttons here */
<?php $this->endWidget(); ?>
Submit with Ajax, in Yii it is easy to do and it will keep things easy to understand in the controllers, each controller will have a save and respond with json to confirm the save. There is already ajax validation.
/**
* Performs the AJAX validation.
* #param CModel the model to be validated
*/
protected function performAjaxValidation($model)
{
if(isset($_POST['ajax']) && $_POST['ajax']==='employee-form')
{
$valid = CActiveForm::validate($model);
if(strlen($valid) > 2) {
echo $valid;
Yii::app()->end();
}
}
}
As you can see I have modified it to return the error if there is one (validate returns [] if it is valid, I should probably check for that instead of strlen >2 ), otherwise let the script continue, in this case it will go to the save function.

In Yii, is there a way to validate tabular input with CActiveForm?

Situation
I used the wiki article on Yii's site, Collecting Tabular Input, to follow as an example.
I don't believe I need to validate tabular input in a traditional sense against multiple models. I only have one model, but I'm dynamically creating the number of fields in the form. Here's a bit more background.
I'm importing CSV files where its headers vary in order among the different files. Before correctly parsing the files, the user needs to map which header would map to what table/column.
I have a single model, ImportParseForm extended from CFormModel. It really only has one rule:
public function rules()
{
return array(
array('header', 'required'),
);
}
Here's a snippet of my view:
<?php foreach($headers as $h => $hItem): ?>
<div class="row">
<?php echo CHtml::label(CHtml::encode($hItem), "[$h]header"); ?> maps to
<?php echo $fParse->textField($mForm, "[$h]header"); ?>
<?php echo $fParse->error($mForm, "[$h]header"); ?>
</div>
<?php endforeach; ?>
Here's a snippet of my controller:
$mForm = new ImportParseForm;
$valid = true;
if (isset($_POST['ImportParseForm'])){
foreach ($headers as $h => $hVal){
if (isset($_POST['ImportParseForm'][$h])){
$mForm->attributes = $_POST['ImportParseForm'][$h];
$valid = $mForm->validate() && $valid;
}
}
if ($valid){
// Process CSV
}
}
If all fields are valid, then it passes as expected. The problem is if one of the fields are invalid (or in this case, empty), then all fields are flagged as invalid.
In Yii 1.1.10, they added CActiveForm::validateTabular(), but it looks like it's for multiple models. Not quite what I have here. But for kicks, I added the following to my controller (removed the other type of validation, of course):
CActiveForm::validateTabular($mForm, array('header'));
The form itself is only valid if the first element is populated. If the first element is populated, it will set all the other elements with that same value (and passes validation).
Question
Basically, can I use CActiveForm to do validation against fields that are dynamically generated (similar to tabular input, but with only one model)?
I was trying to do similar thing, and this is my solution in case of model update form. Here I exploited model validation for update model attributes on value change event, which don't need submit button, and looks simple and fancy. Here is code snippet...
view code:
<?php foreach($modelArray as $model): ?>
<div class="row">
<?php echo $form->textField($model, "[$model->id]attributeName"); ?>
<?php echo $form->label($model, "[$model->id]attributeName"); ?>
<?php echo $form->error($model, "[$model->id]attributeName"); ?>
</div>
<?php endforeach; ?>
controller code:
objArray = array();
foreach($_REQUEST['ModelName'] as $id => $attributes){
$obj = ModelName::model()->findByPk($id);
$obj->attributes = $attributes;
$obj->save();
$objArray[$id] = $obj;
}
echo CActiveForm::ValidateTabular($objArray);
Yii::app()->end();
After reading Collecting Tabular Input a bit closer, I am using "multiple" models. I misunderstood that multiple models would mean multiple different structured models and not just multiple of the same structured model in an array. For example, in the wiki there's a piece that shows what items (array of models) to update: $items=$this->getItemsToUpdate();. My corrected assumption is that that particular method grabs multiple of the same structured model but with different primary keys... or different records. Understanding that, the rest of the article makes more sense ;)
But here's my model solution on how to create a CSV header mapping form.
class ImportParseForm extends CFormModel{
// Model really only has one attribute to check against, the header
var $header;
// New attributeLabels collected and stored on class instantiation
protected $attributeLabels;
// Modify construct so we can pass in custom attribute labels
public function __construct($attributeLabels = '', $scenario = '')
{
if (! is_array($attributeLabels)){
$this->attributeLabels = array($attributeLabels);
}
else{
$this->attributeLabels = $attributeLabels;
}
parent::__construct($scenario);
}
public function rules()
{
return array(
array('header', 'required'),
);
}
public function attributeLabels()
{
// Default mapping
$arr = array(
'header' => 'Header Mapping',
);
// Merge mapping where custom labels overwrite default
return array_merge($arr, $this->attributeLabels);
}
}
Here is a snippet in my controller on what my equivalent to $items=$this->getItemsToUpdate(); (again, the goal is to collect an array of models) would look like
// Get the first row of CSV, assume it's the headers
$tmpCsvRow = explode("\n", $mTmp->data);
$headers = explode(',', $tmpCsvRow[0]);
foreach ($headers as $header){
if (! empty($header)){ // Blank headers are lame, skip them
// Add a new model for each CSV header found into $mForm array
// You can also add in a custom attributeLabel, $header is an actual header name like 'First Name',
// so the new label for the header attribute in ImportParseForm would be 'First Name header' and
// it will show up properly in your CActiveForm view
$mForm[] = new ImportParseForm(array('header' => $header.' header'));
}
}
Push $mForm to your view. Now in your view, iterate through $mForm for your form like so (similar to the wiki article, but I'm using a CActiveForm widget here):
<?php foreach($mForm as $m => $mItem): ?>
<div class="row">
<?php echo $fParse->labelEx($mItem,"[$m]header"); ?> maps to
<?php echo $fParse->textField($mItem, "[$m]header"); ?>
<?php echo $fParse->error($mItem, "[$m]header"); ?>
</div>
<?php endforeach; ?>
Validation works as expected.
If you want to use AJAX validation, use CActiveForm::validateTabular() in your controller (instead of the normal validate()).
Hope this helps other Yii beginners! :)

how to ajaxify zend_pagination results (already working with partialoop) using jquery

in the controller i have :
$paginator = Zend_Paginator::factory($mdlPost->getPosts($this->moduleData->accordion, 'name ASC'));
if(isset($params['cities'])) {
$paginator->setCurrentPageNumber(intval($params['cities']));
}
$paginator->setItemCountPerPage(4);
$this->view->posts = $paginator;
in the view's i have some thing like this :
if ($this->posts != null) {?>
<div id="cities_accord" class="news">
<?php echo $this->partialLoop('partials/post-min.phtml', $this->posts); ?>
</div>
<?php echo $this->paginationControl($this->posts,
'Sliding',
'public/pagination_cont.phtml');
}
the partial/post-min.phtml
<?php
$color = array(1=>'spring',2=>'summer',3=>'autumn',4=>'winter');
?>
<div id='<?php echo $color[$this->partialCounter] ?>' class="accordion_post">
<?php
$link = Digitalus_Uri::get(false, false, array('openCity' =>
$this->id));//$color[$this->partialCounter]));
?>
<h1 class="accordion_post_title"><?php echo $this->title ?></h1>
<p><?php echo $this->teaser ?> <i>read more</i></p>
</div>
the pagination_cont.phtml taken from this link zend ( http://framework.zend.com/manual/en/zend.paginator.usage.html )
will show links that will pass params to the controller to fetch the corresponding whole page which is working alright for now
but i want to change this so that i will be able ajaxify the returned ( i.e. only a single paginated value rather than reloading the whole page ) results how can i do that using jquery and what should i change ..
** EDIT: it would be nice to have a fail-save ,if possible, for browsers(users) that disabled javascript to see the same thing by reloading the page (i.e. keeping the current status for if(javascript_not_enabled ))**
This is what I've done in the past.
First, setup the AjaxContext action helper to enable the html context on your controller action.
Add a .ajax.phtml view that just contains the section of markup that may be replaced via AJAX as well as the pagination control links. You can probably just copy this out of your normal view. Replace that section in your normal view with something like
<div id="reloadable-content">
<?php echo $this->render('controller/action.ajax.phtml') ?>
</div>
This will ensure that your initial and any non-AJAX requests will still include the right content. The <div> id is purely for referencing the loadable block in JavaScript.
Also make sure you include your JS file (using headScript) in the normal view only.
Now, in your JS file, unobtrusively add the appropriate event binding to the paginator links. As you'll be replacing the pagination control section in order to reflect the correct current page and other links, it's probably best to do this using the jQuery live binding. I'm also assuming you'll wrap the pagination control with some kind of identifiable element (<div class="pagination-control"> for example)
$('.pagination-control').find('a').live('click', function(e) {
var link = $(this);
$('#reloadable-content').load(link.attr('href'), { format: 'html' });
return false;
});
Keep in mind that in using this method, you will lose the ability to navigate the paged requests using the normal back / forward browser buttons. You will also lose the ability to bookmark pages directly (though you could always provide a permanent link to the current page as part of the AJAX loaded content).
You can use something like the jQuery history plugin if you're really concerned but that will require more client-side work.
Another caveat is that the above will only work with pagination links. If you want to use a form with dropdown page selection, you need to add another event handler for the submission.
GOT IT and big Thanks to #Phil Brown :
in the controller int() change the response type to json
class NewsController extends Zend_Controller_Action
{
public function init()
{
$contextSwitch = $this->_helper->getHelper('contextSwitch');
$contextSwitch->addActionContext('list', 'JSON')
->initContext();
}
// ...
}
public listAtcion() {
// .............
$paginator = Zend_Paginator::factory($mdlPost->getPosts($this->moduleData->accordion, 'name ASC'));
if(isset($params['cities'])) {
$paginator->setCurrentPageNumber(intval($params['cities']));
}
$paginator->setItemCountPerPage(4);
$post = array();
foreach($paginator as $post ) {
$post[] = $post;
}
$this->view->post = $paginator;
#TODO //add a check here for non-ajax requests (#improvment)
$this->view->posts = $paginator;
}
in one of the views (most probably in the pagination_cont.phtml) on the pagination controller add the ajax links
<?= $this->ajaxLink (
$this->url('cities'=>$this->page_num),array('id'=>'div_id','complete'=>'js_method(json_data)','method'=>post) ,array('format'=>'JSON'));
and add a JavaScript function of js_method(json_data) to modify the div with id = 'div_id' with a json data
function js_method(json_data) {
var content = parse.JSON(json_data);
$('#div_id').html('');
//fill it with the reposnse content some thing like $('#div_id').append(content);
}

Categories