Dynamic form in Yii - ajax

I'm having a problem creating something for my application. What i already have is a CJuiTabs system with a form in each tab. The number and values/names of the tabs is defined by a number of tinyInt columns in an another table in my database. The value of the tab is submitted with the form as a hidden field.
So now if you go to a view with an id you have already submitted a record with, you will be able to update that record, but you are not able to create new records with the other tab-values.
This is my problem. How do i make the application decide wether to create a new record or update an existing one based on which tab you have selected? If you are updating a record i would like to have the values of the record shown, but if you are creating a new one you should only see blank fields (well, its a rating system, so i guess it is stars not fields).
I guess it chould be done by using AJAX but i'm very much unsure of how to do that.
Game/view:
<?php
$tab_list=Platform::getPlatforms();
$tabarray=array();
// Create Dynamic Tabs
foreach($tab_list as $key=>$value){
$tabarray["$value"]=array(
'id'=>$key,
'content'=>$this->renderPartial('../ranking/_form',
array('model'=>$ranking, 'key'=>$key),TRUE)
);
}?>
<?php
$this->widget('zii.widgets.jui.CJuiTabs',array(
'tabs'=>$tabarray,
'options'=>array(
'collapsible'=>true,
),
'id'=>'categorytabs',
)); ?>
Models/Platform:
const PC = 1;
const Mac = 2;
const XBOX = 3;
const XBOX360 = 4;
const PS2 = 5;
const PS3 = 6;
const PSP = 7;
const PSVITA = 8;
const Wii = 9;
const WiiU = 10;
const NintendoDS = 11;
const NintendoDS3 = 12;
...
public function getPlatforms()
{
$id = Yii::app()->request->getQuery('id');
$platform = Platform::model()->findByPk($id);
$platforms = array();
if ($platform -> pc == 1)
{
$platforms[self::PC] = "PC";
}
if ($platform -> xbox == 1)
{
$platforms[self::XBOX] = 'XBOX';
}
if ($platform -> xbox360 == 1)
{
$platforms[self::XBOX360] = "XBOX 360";
}
if ($platform -> ps2 == 1)
{
$platforms[self::PS2] = "PS2";
}
if ($platform -> ps3 == 1)
{
$platforms[self::PS3] = 'PS3';
}
if ($platform -> psp == 1)
{
$platforms[self::PSP] = "PSP";
}
if ($platform -> psVita == 1)
{
$platforms[self::PSVITA] = 'PS VITA';
}
if ($platform -> wii == 1)
{
$platforms[self::Wii] = "Wii";
}
if ($platform -> wiiU == 1)
{
$platforms[self::WiiU] = "Wii U";
}
if ($platform -> nintendoDS == 1)
{
$platforms[self::NintendoDS] = 'Nintendo DS';
}
if ($platform -> nintendoDS3 == 1)
{
$platforms[self::NintendoDS3] = 'Nintendo DS3';
}
return $platforms;
}
Controllers/GameController:
public function actionView($id)
{
...
$ranking=$this->createRanking($model);
...
}
protected function createRanking($model)
{
$user_id=Yii::app()->user->getId();
$game_id=$model->id;
$rank=ranking::model()->find("create_user_id=$user_id and game_id=$game_id");
if($rank===null){
$ranking=new Ranking;
}
else{
$ranking=$rank;
}
if(isset($_POST['Ranking']))
{
$ranking->game_id=$model->id;
$ranking->attributes=$_POST['Ranking'];
$valid = $ranking->validate();
if ($valid)
{
$ranking->save(false);
$this->redirect(array('index'));
}
}
return $ranking;
}
Ranking/_form:
<?php $form=$this->beginWidget('CActiveForm', array(
'id'=>'ranking-form',
'enableAjaxValidation'=>false,
)); ?>
<p class="note">Fields with <span class="required">*</span> are required.</p>
<?php echo $form->errorSummary($model); ?>
<?php echo $form->hiddenField($model, 'platform_id', array('value' => $key)); ?>
<div class="row">
<?php echo $form->labelEx($model,'overall'); ?>
<?php $this->widget('CStarRating',array(
'model'=>$model,
'attribute' => 'overall',
)); ?>
<?php echo $form->error($model,'overall'); ?>
</div>
<br/>
<div class="row">
<?php echo $form->labelEx($model,'graphics'); ?>
<?php $this->widget('CStarRating',array(
'model'=>$model,
'attribute' => 'graphics',
)); ?>
<?php echo $form->error($model,'graphics'); ?>
</div>
<br/>
<div class="row">
<?php echo $form->labelEx($model,'sound'); ?>
<?php $this->widget('CStarRating',array(
'model'=>$model,
'attribute' => 'sound',
)); ?>
<?php echo $form->error($model,'sound'); ?>
</div>
<br/>
<div class="row">
<?php echo $form->labelEx($model,'gameplay'); ?>
<?php $this->widget('CStarRating',array(
'model'=>$model,
'attribute' => 'gameplay',
)); ?>
<?php echo $form->error($model,'gameplay'); ?>
</div>
<br/>
<div class="row">
<?php echo $form->labelEx($model,'lastingApp'); ?>
<?php $this->widget('CStarRating',array(
'model'=>$model,
'attribute' => 'lastingApp',
)); ?>
<?php echo $form->error($model,'gameplay'); ?>
</div>
<br/>
<div class="row buttons">
<?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>
</div>
<?php $this->endWidget(); ?>

I'm not sure I get this completely, let me try to see if I get the problem,
You have multiple forms one user can edit, they're all forms to rank one game on different gaming platforms (XBOX, PS2/3, etc..).
If this is the case, in your Game controller:
$rank=ranking::model()->find("create_user_id=$user_id and game_id=$game_id");
will always return the same db entry regardless of gaming platform (provided there is an entry)
You would need to use something along the lines of
$rank=ranking::model()->find("create_user_id=$user_id and game_id=$game_id and platform_id=$platform_id");
That covers the saving part but there's also an issue in the displaying. You have:
foreach($tab_list as $key=>$value){
$tabarray["$value"]=array(
'id'=>$key,
'content'=>$this->renderPartial('../ranking/_form',
array('model'=>$ranking, 'key'=>$key),TRUE)
);
In essence, you use the same $ranking model for each tab, so therefore the same (already existing) field inputs, or star ratings.
This is where it gets a little tricky. Ideally you would use Yii's relations To set up your models as Plateforms having multiple Games(MANY_MANY) that have multiple Ratings depending on Plateform/Game combination. Then you could do something along the lines of :
$platforms = Plateform::model()->findAll(array(
'with'=>array(
'rankings'=>array(
'condition'=>'game_id=$game_id AND platform_id=t.id')) //t refers to platform in Yii < 1.1.13. For Yii >= 1.1.13 check version update logs
));
foreach($platforms as $platform){
$tabarray[$platform->title]=array( //called it title arbitrarily, could be anything
'id'=>$platform->id,
'content'=>$this->renderPartial('../ranking/_form',
array('model'=>$platform->rankings[0], 'key'=>$platform->id),TRUE)
);
(I'm sparing you the use of 'params'=>array(), but do make it a habit.)
However you don't seem like you've gone that route, so check it out if you want, if not how about changing createRanking() to return an array of rankings corresponding to a platform for a given game using a key=> value where the key is the platform id. Then you could do:
foreach($tab_list as $key=>$value){
$tabarray["$value"]=array(
'id'=>$key,
'content'=>$this->renderPartial('../ranking/_form',
array('model'=>$rankings[$key], 'key'=>$key),TRUE)
);
If you went with that, as far as saving goes, if isset($_GET['Ranking']) then you would use the platform_id to isolate the correct model from the array in order to save it to db (or create a new one if it doesn't exist)
Hope that helps, or at least gets you on the right track.
edit: Added some code, not the best/cleanest solution but it should work and be easy to understand :
Controllers/GameController:
public function actionView($id)
{
...
$this->createRanking($model);
$rankings = $this->getRankingList();
...
}
//add this function
protected function getRankingList($gid,$uid)
{
$tab_list=Platform::getPlatforms();
$rankings = array();
foreach($tab_list as $key=>$value)
{
$rank=Ranking::model()->find("create_user_id=$user_id and game_id=$game_id and platform_id=$key");
if(empty($rank))
$rank = new Ranking;
$rankings[$key]= $rank;
}
return $rankings;
}
Game/view:
<?php
$tab_list=Platform::getPlatforms();
$tabarray=array();
// Create Dynamic Tabs
foreach($tab_list as $key=>$value){
$tabarray["$value"]=array(
'id'=>$key,
'content'=>$this->renderPartial('../ranking/_form',
array('model'=>$rankings[$key], 'key'=>$key),TRUE)
);
}?>
<?php
$this->widget('zii.widgets.jui.CJuiTabs',array(
'tabs'=>$tabarray,
'options'=>array(
'collapsible'=>true,
),
'id'=>'categorytabs',
)); ?>

Related

get all Categories and SubCatgories without loading model in foreach loop

I want to display all categories and their subcategories in primary nav menu.
When i hover on cateory, it should display its subcategories.
I want to implement this functionality without loading Mage::getModel('catalog/category') in foreach loop.
as you want to write code in phtml file use below code which create tree structure of category.
<ul>
<?php
$obj = new Mage_Catalog_Block_Navigation();
$storeCategories = $obj->getStoreCategories();
Mage::registry('current_category') ? $currentCategoryId = Mage::registry('current_category')->getId() : $currentCategoryId='';
foreach ($storeCategories as $_category):
?>
<li>
<strong><?php echo $_category->getName(); ?></strong>
<?php $categoryChildren = $_category->getChildren(); ?>
<?php if($categoryChildren->count()) : ?>
<ul>
<?php foreach($categoryChildren as $_categoryChild) : ?>
<?php $_categoryChildModel = Mage::getModel('catalog/category')->load($_categoryChild->getId());?>
<?php $categoryGrandchildren=$_categoryChild->getChildren(); ?>
<li>
<?php
$currentCategoryId===$_categoryChild->getId() ? $bold="style=\"font-weight:bold\"" : $bold='';
echo ' ' . '<a href="' . $_categoryChildModel->getUrl() . '"' . $bold . '>' . $_categoryChild->getName() . '(' . $_categoryChildModel->getProductCollection()->count() . ')</a>';
?>
</li>
<?php if($categoryGrandchildren->count()) : ?>
<?php foreach($categoryGrandchildren as $_categoryGrandchild) : ?>
<?php $_categoryGrandchildModel = Mage::getModel('catalog/category')->load($_categoryGrandchild->getId());?>
<li>
<?php
$currentCategoryId===$_categoryChild->getId() ? $bold="style=\"font-weight:bold\"" : $bold='';
echo '  ' . '<a href="' . $_categoryGrandchildModel->getUrl() . '"' . $bold . '>' . $_categoryGrandchild->getName() . '(' . $_categoryGrandchildModel->getProductCount() . ')</a>';
?>
</li>
<?php endforeach; ?>
<?php endif; ?>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</li>
<?php endforeach ?>
</ul>
and using css and HTML you can achieve your goal to display sub menu on hover of main menu.
Still let me if you need any other help.
Thanks
ok so I just did this and I figured I'd search to see if anyone was wondering how to achieve this. The trick to this is
Mage::getResourceSingleton('catalog/category')->getAttributeRawValue($categoryEntityId,array('name','level','url_key','path','is_active'),Mage::app()->getStore());
This does not load the category model lets take a look at what it is actually doing.
Go to app/code/core/Mage/Catalog/Model/Resource/Abstract
public function getAttributeRawValue($entityId, $attribute, $store)
{
if (!$entityId || empty($attribute)) {
return false;
}
if (!is_array($attribute)) {
$attribute = array($attribute);
}
$attributesData = array();
$staticAttributes = array();
$typedAttributes = array();
$staticTable = null;
$adapter = $this->_getReadAdapter();
foreach ($attribute as $_attribute) {
/* #var $attribute Mage_Catalog_Model_Entity_Attribute */
$_attribute = $this->getAttribute($_attribute);
if (!$_attribute) {
continue;
}
$attributeCode = $_attribute->getAttributeCode();
$attrTable = $_attribute->getBackend()->getTable();
$isStatic = $_attribute->getBackend()->isStatic();
if ($isStatic) {
$staticAttributes[] = $attributeCode;
$staticTable = $attrTable;
} else {
/**
* That structure needed to avoid farther sql joins for getting attribute's code by id
*/
$typedAttributes[$attrTable][$_attribute->getId()] = $attributeCode;
}
}
/**
* Collecting static attributes
*/
if ($staticAttributes) {
$select = $adapter->select()->from($staticTable, $staticAttributes)
->where($this->getEntityIdField() . ' = :entity_id');
$attributesData = $adapter->fetchRow($select, array('entity_id' => $entityId));
}
/**
* Collecting typed attributes, performing separate SQL query for each attribute type table
*/
if ($store instanceof Mage_Core_Model_Store) {
$store = $store->getId();
}
$store = (int)$store;
if ($typedAttributes) {
foreach ($typedAttributes as $table => $_attributes) {
$select = $adapter->select()
->from(array('default_value' => $table), array('attribute_id'))
->where('default_value.attribute_id IN (?)', array_keys($_attributes))
->where('default_value.entity_type_id = :entity_type_id')
->where('default_value.entity_id = :entity_id')
->where('default_value.store_id = ?', 0);
$bind = array(
'entity_type_id' => $this->getTypeId(),
'entity_id' => $entityId,
);
if ($store != $this->getDefaultStoreId()) {
$valueExpr = $adapter->getCheckSql('store_value.value IS NULL',
'default_value.value', 'store_value.value');
$joinCondition = array(
$adapter->quoteInto('store_value.attribute_id IN (?)', array_keys($_attributes)),
'store_value.entity_type_id = :entity_type_id',
'store_value.entity_id = :entity_id',
'store_value.store_id = :store_id',
);
$select->joinLeft(
array('store_value' => $table),
implode(' AND ', $joinCondition),
array('attr_value' => $valueExpr)
);
$bind['store_id'] = $store;
} else {
$select->columns(array('attr_value' => 'value'), 'default_value');
}
$result = $adapter->fetchPairs($select, $bind);
foreach ($result as $attrId => $value) {
$attrCode = $typedAttributes[$table][$attrId];
$attributesData[$attrCode] = $value;
}
}
}
if (sizeof($attributesData) == 1) {
$_data = each($attributesData);
$attributesData = $_data[1];
}
return $attributesData ? $attributesData : false;
}
As you can see no model loading happening just retrieving specific pieces of info. Also being part of the resource abstract means all catalog resource models (I haven't checked other resource models but, I wouldn't be too surprised to find this in others) have this available.
If you use this in an override of Mage_Catalog_Block_Navigation you can then call all of the info you need about any category without loading the model. To traverse the tree however, you have to do terrible things.
You can use 'path'(explode on /) to easily retrieve parents but you will need to get dirty in order to retrieve children categories so something like this to get Children.
$childrenQuery = "SELECT entity_id FROM catalog_category_entity WHERE path REGEXP '^.*\/" . $categoryId . "\/[[:digit:]]?[[:digit:]]?[[:digit:]]?[[:digit:]]?$'";
$resource = Mage::getSingleton('core/resource');
$readCxn = $resource->getConnection('core/read');
$children = $readCxn->fetchAll($childrenQuery);
if ($children[0]) {
return $children;
} else {
return;
}
The overall difficulty is that all model and resource model functions will expect an instance of a category object to make them all work with just the entity_id is definitely possible just a pain.
So I would not recommend doing this in general the only reason I did this is because the default magento root category in my case was not the actual functional root of categories (fun eh). If you are using a standard root category I would recommend using the Helper functions and retrieving the info from cache.
Either way from here all you have to do complete your functions in Mage_Catalog_Block_Navigation and assemble your menu in a template. And there you go; complete category menu without ever accessing a model->load.
Try This Code
<?php
require_once("app/Mage.php");
Mage::app();
function getChildCategories($category, $First = false) {
$sub = $First ? $category : $category->getChildren();
foreach ($sub as $child) {
$_categories[] = [ "name" => $child->getName(), "id" => $child->getId(), "children" => getChildCategories($child) ];
}
return $_categories;
};
function CategoriesTree($category, $First = false) {
$sub = $First ? $category : $category->getChildren();
echo "<pre>";
foreach ($sub as $child) {
echo $child->getName(); ;
CategoriesTree($child);
}
}
$_categories = Mage::helper('catalog/category')->getStoreCategories();
$categories = getChildCategories($_categories, true);
CategoriesTree($_categories, true);
?>

Yii2 update related model makes insert

Depending on this question: Yii2 updating two related models does not show data of the second. I have manged calling the related model InvoiceItems to the Invoices model it hasMany relation.
However, updating leads to insert new records in invoice_items table instead of updating the current related records to the invoices table.
I tried to add the id field of each InvoiceItems record in the _form view to solve this issue, but it still exist.
The following is actionUpdate of the InvoicesController:
public function actionUpdate($id)
{
$model = $this->findModel($id);
//$invoiceItems = new InvoiceItems();
$count = count(Yii::$app->request->post('InvoiceItems', []));
//Send at least one model to the form
$invoiceItems = [new InvoiceItems()];
//Create an array of the products submitted
for($i = 1; $i < $count; $i++) {
$invoiceItems[] = new InvoiceItems();
}
if ($model->load(Yii::$app->request->post()) && $model->save()) {
//$invoiceItems->invoice_id = $model->id;
if (Model::loadMultiple($invoiceItems, Yii::$app->request->post())){
foreach ($invoiceItems as $item){
$item->invoice_id = $model->id;
//$item->id = $model->invoiceItems->id;
$item->save(false);
}
return $this->redirect(['view', 'id' => $model->id]);
}
else{
return var_dump($invoiceItems);
}
} else {
//$invoiceItems->invoice_id = $model->id;
$invoiceItems = $this->findInvoiceItemsModel($model->id);
return $this->render('update', [
'model' => $model,
'invoiceItems' => $invoiceItems,
]);
}
}
This is the code of _form view of InvoicesController:
<div class="invoices-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'created')->textInput() ?>
<?= $form->field($model, 'type')->textInput(['maxlength' => true]) ?>
<hr />
<?php if (is_array($invoiceItems)): ?>
<?php foreach ($invoiceItems as $i => $item): ?>
<?= $form->field($item, "[$i]id")->textInput();?>
<?= $form->field($item, "[$i]item_id")->textInput();?>
<?= $form->field($item, "[$i]unit_id")->textInput();?>
<?= $form->field($item, "[$i]qty")->textInput();?>
<?php endforeach; ?>
<?php else: ?>
<?= $form->field($invoiceItems, "item_id")->textInput();?>
<?= $form->field($invoiceItems, "unit_id")->textInput();?>
<?= $form->field($invoiceItems, "qty")->textInput();?>
<?php endif; ?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? Yii::t('app', 'Create') : Yii::t('app', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
The following screen shot, visually, demonstrates what I have got:
if you in update you don't need new record you should remove
this part
$count = count(Yii::$app->request->post('InvoiceItems', []));
//Send at least one model to the form
$invoiceItems = [new InvoiceItems()];
//Create an array of the products submitted
for($i = 1; $i < $count; $i++) {
$invoiceItems[] = new InvoiceItems();
}
The $invoiceItems you create in this way are obviuosly "new" and then are inserted ..for an update the model must to be not new..
You have already the related models to save they came from post.. and from load multiple
If you however need to manage new model (eg: because you added new record in wide form update operation) you can test
$yourModel->isNewRecord
and if this is new check then if the user have setted properly the related fields you can save it with
$yourModel->save();
otherwise you can simple discart.. (not saving it)
I am pretty sure that this code below of course fills a array named $invoiceItems with the submitted data.
if (Model::loadMultiple($invoiceItems, Yii::$app->request->post())){
foreach ($invoiceItems as $item){
$item->invoice_id = $model->id;
//$item->id = $model->invoiceItems->id;
$item->save(false);
}
}
But all $items have the scenario "insert" (could be that you allow the setting of the attribute 'ID' but normally this isn't allowed in the Gii generated code. and then you get "new items" whenever you save.
if you add a return var_dump($invoiceItems);after the loadMultiple you will see that only the safe attributes are filled with the submitted data.
You are also not validating them before saving, which is also kind of bad.
if (Model::loadMultiple($invoiceItems, Yii::$app->request->post()) && Model::validateMultiple($invoiceItems)) {
http://www.yiiframework.com/doc-2.0/guide-input-tabular-input.html
According to scaisEdge answer and this widget documentation. I could able to determine the problem solution.
Simply, in my code I neglected the relation between the two models, when I say:
$count = count(Yii::$app->request->post('InvoiceItems', []));
//Send at least one model to the form
$invoiceItems = [new InvoiceItems()];
The value of $invoiceItems should be obtained using the relation like the following:
$invoiceItems = $model->invoiceItems;
However, I still have another issue with adding new records to the related model InvoiceItems during the update.

Duplicate category programmatically

I'm on Magento 1.4.1.1. How can I duplicate category programmatically using its id number?
<?php set_time_limit(0); ?>
<?php ob_end_flush(); ?>
<?php ob_implicit_flush(); ?>
<?php require_once 'app/Mage.php'; ?>
<?php Mage::app(); ?>
<?php define('STARTTIME', microtime(true)); ?>
<?php $availSortBy = array() ?>
<?php foreach (Mage::getSingleton('catalog/config')->getAttributeUsedForSortByArray() as $key => $value){$availSortBy[] = $key;} ?>
<?php define('AVAILSORTBY', implode(",",$availSortBy)); ?>
<?php var_dump(AVAILSORTBY) ?>
<?php
function buildTree($category,$return=array()){
if($category->getChildrenCount() !== "0"){
$return[$category->getId()] = array();
foreach($category->getChildrenCategories() as $child){
$return[$category->getId()][$child->getId()] = buildTree($child);
}
}else{
$return = $category->getId();
}
return $return;
}
function processTree($tree,$parentId){
$first=false;
foreach ($tree as $catId => $data) {
if($first){
$newParentId = $parentId;
$first = false;
}else{
// EXCLUDE COLLECTION
$newParentId = createCategory($catId,$parentId);
// INCLUDE COLLECTION
// $newParentId = createCategory($catId,$parentId,true);
}
if(is_array($data)){
processTree($data,$newParentId);
}
}
}
function createCategory($oldCategoryId,$parent_id,$includeCollection=false){
try{
// LOAD MODELS
$oldCategory = Mage::getModel('catalog/category')->load($oldCategoryId);
// SET VALUES
$newData = $oldCategory->getData();
foreach (array('entity_id','parent_id','created_at','updated_at','path','children_count') as $unset) {
unset($newData[$unset]);
}
foreach (array('available_sort_by'=>AVAILSORTBY,'default_sort_by'=>'name','include_in_menu'=>'0','is_active'=>'1','name'=>'Unnamed Category') as $req=>$default) {
$newData[$req] = empty($newData[$req]) ? $default : $newData[$req];
}
$newId = Mage::getSingleton('catalog/category_api')->create($parent_id,$newData);
$newCategory = Mage::getModel('catalog/category')->load($newId);
// COLLECTION
if($includeCollection){
$collectionCount = 0;
foreach ($oldCategory->getProductCollection() as $_product) {
$collectionCount++;
Mage::getSingleton('catalog/category_api')->assignProduct($newCategory->getId(),$_product->getId());
}
$collectionString = "<p>".$collectionCount." Products Added</p>";
}else{
$collectionString = "";
}
// RUN TIME
$duration = number_format(((microtime(true)-STARTTIME)/60),4);
// OUTPUT
echo "<p>New Category: <strong>".$newCategory->getName()."</strong> (".$newCategory->getUrlPath().")</p>".$collectionString."<small>DURATION:".$duration." mins</small><hr>";
ob_flush();
return $newCategory->getId();
} catch(Exception $e) {
print_r($e);
}
return false;
}
?>
<?php if(!empty($_REQUEST['category-id']) && !empty($_REQUEST['target-parent-id'])):?>
<?php $oldCategory = Mage::getModel('catalog/category')->load($_REQUEST['category-id']); ?>
<?php if($oldCategory): ?>
<?php $catTree = buildTree($oldCategory); ?>
<?php processTree($catTree,$_REQUEST['target-parent-id']); ?>
<script>
var iFrequency = 500; // expressed in miliseconds
var interval = 0;
function startLoop() {
if(interval > 0) clearInterval(interval); // stop
interval = setInterval( "window.scrollTo(0,document.body.scrollHeight)", iFrequency );
}
startLoop();
</script>
<?php else: ?>
<h1>Invalid Category</h1>
<?php endif; ?>
<?php else: ?>
<h1>Missing Parameters</h1>
<?php endif; ?>
Check out the attached script. If you upload it to your root folder and access it, including the URL params:
category-id={ID OF THE CATEGORY YOU WOULD LIKE TO COPY}
target-parent-id={ID OF THE TARGET CATEGORY}
NOTE: I included (but commented out) an alternate function call that would include the product collection as well in the category but because we use a dynamic script at my company it wasn't necessary for me to include the collection data. You can uncomment line 39 and comment out line 37 to include the category product collection.
$category_to_duplicate = Mage::getModel('catalog/category')->load($cat_id);
$category_new = Mage::getModel('catalog/category');
$category_new->setStoreId(0); // 0 = default/all store view.
$category_new->addData($category_to_duplicate->getData());
try {
$category->save();
echo "Success! Id: ".$category_new->getId();
}
catch (Exception $e){
echo $e->getMessage();
}

Flash messanger in zf2

How can i use flash messenger in zend freamwork 2? Session documentation is not yet. Anyone know it? But session libraries are there.
Update :
Zend Framework new release added FlashMessenger View Helper , found in path /library/Zend/View/Helper/FlashMessenger.php
FlashMessenger.php
Old answer :
I have written a custom view helper, for printing flash messages
In /module/Application/Module.php
public function getViewHelperConfig()
{
return array(
'factories' => array(
'flashMessage' => function($sm) {
$flashmessenger = $sm->getServiceLocator()
->get('ControllerPluginManager')
->get('flashmessenger');
$message = new \My\View\Helper\FlashMessages( ) ;
$message->setFlashMessenger( $flashmessenger );
return $message ;
}
),
);
}
Create a custom view helper in /library/My/View/Helper/FlashMessages.php
namespace My\View\Helper;
use Zend\View\Helper\AbstractHelper;
class FlashMessages extends AbstractHelper
{
protected $flashMessenger;
public function setFlashMessenger( $flashMessenger )
{
$this->flashMessenger = $flashMessenger ;
}
public function __invoke( )
{
$namespaces = array(
'error' ,'success',
'info','warning'
);
// messages as string
$messageString = '';
foreach ( $namespaces as $ns ) {
$this->flashMessenger->setNamespace( $ns );
$messages = array_merge(
$this->flashMessenger->getMessages(),
$this->flashMessenger->getCurrentMessages()
);
if ( ! $messages ) continue;
$messageString .= "<div class='$ns'>"
. implode( '<br />', $messages )
.'</div>';
}
return $messageString ;
}
}
then simple call from layout.phtml , or your view.phtml
echo $this->flashMessage();
Let me show example of controller action
public function testFlashAction()
{
//set flash message
$this->flashMessenger()->setNamespace('warning')
->addMessage('Mail sending failed!');
//set flash message
$this->flashMessenger()->setNamespace('success')
->addMessage('Data added successfully');
// redirect to home page
return $this->redirect()->toUrl('/');
}
In home page, it prints
<div class="success">Data added successfully</div>
<div class="warning">Mail sending failed!</div>
Hope this will helps !
i have written a post about this some time ago. You can find it right here
Basically you use it just the same like earlier.
<?php
public function commentAction()
{
// ... display Form
// ... validate the Form
if ($form->isValid()) {
// try-catch passing data to database
$this->flashMessenger()->addMessage('Thank you for your comment!');
return $this->redirect()->toRoute('blog-details'); //id, blabla
}
}
public function detailsAction()
{
// Grab the Blog with given ID
// Grab all Comments for this blog
// Assign the view Variables
return array(
'blog' => $blog,
'comments' => $comments,
'flashMessages' => $this->flashMessenger()->getMessages()
);
}
Then in your .phtml file you do it like this:
// details.phtml
<?php if(count($flashMessages)) : ?>
<ul>
<?php foreach ($flashMessages as $msg) : ?>
<li><?php echo $msg; ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
Obviously this isn't all too handy, as you have to do this for every single .phtml file. Therefore doing it within the layout you have to do it at best like the following:
<?php
// layout.phtml
// First get the viewmodel and all its children (ie the actions viewmodel)
$children = $this->viewModel()
->getCurrent()
->getChildren();
$ourView = $children[0];
if (isset($ourView->flashMessages) && count($ourView->flashMessages)) : ?>
<ul class="flashMessages">
<?php foreach ($ourView->flashMessages as $fMessage) : ?>
<li><?php echo $fMessage; ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
If you need further description, please see my blog, but i guess the code itself is pretty clear (apart frmo the layout.phtml example). Alternatively you're always free to write your own view helper to have it look a little cleaner inside your view-templates.
How to grab Flashmessenger’s messages in a View Helper – sharing code as requested by Sam.
The View helper should implement the ServiceManagerAwareInterface interface and related methods. The plugin will now have access to a Service Manager which we can use to get the Service Locator and ultimately access to the Flash Messenger.
I’ve not touched this code since I initially wrote it – so there may be a more elegant way of doing this.
protected function getMessages()
{
$serviceLocator = $this->getServiceManager()->getServiceLocator();
$plugin = $serviceLocator->get('ControllerPluginManager');
$flashMessenger = $plugin->get('flashmessenger');
$messages = $flashMessenger->getMessages();
// Check for any recently added messages
if ($flashMessenger->hasCurrentMessages())
{
$messages += $flashMessenger->getCurrentMessages();
$flashMessenger->clearCurrentMessages();
}
return $messages;
}
And calling getMessages() from within the plugin should return an array of messages that can be passed to a partial and rendered.
Add code below to the view to render error messages:
<?php echo $this->flashmessenger()
->setMessageOpenFormat('<div class="alert alert-danger"><ul%s><li>')
->setMessageCloseString('</li></ul></div>')
->render('error')
; ?>
In previous request, make sure you created an error message by running code below in your controller:
$this->flashmessenger()->addErrorMessage('Whops, something went wrong...');

cakephp Ajax: city select box not populating when state is selected

I'm trying to populate the city select box based on the selected state (which is also a select box) using Ajax. When i select state, the city select box is not being populated.
There are 5 models below: Student, MerryParent, MerryClass, State, City. All of them are related to one another.
Can someone please tell me on what am I doing wrong? thank you.
The following are the models:
student.php
<?php
class Student extends appModel{
var $name='Student';
var $belongsTo=array('MerryParent','State','City','MerryClass');
}
?>
merry_parent.php
<?php
class MerryParent extends appModel{
var $name='MerryParent';
$hasMany=array(
'Student'=>array(
'className'=>'Student',
'foreignKey'=>'merry_parent_id'
)
);
$belongsTo=array('State','City','MerryClass');
?>
merry_class.php
<?php
class MerryClass extends AppModel{
var $name='MerryClass';
var $hasMany=array
('Student'=>array(
'className'=>'Student',
'foreignKey'=>'class_id'
),
'MerryParent'
);
var $belongsTo=array('State','City');
//var $displayField='class_name';
}
?>
city.php
<?php
class City extends AppModel{
var $name='City';
var $belongsTo='State';
var $hasMany=array('MerryParent','MerryClass',
'Student'=>array(
'className'=>'Student',
'foreignKey'=>'city_id'
)
);
}
?>
state.php
<?php
class State extends AppModel{
var $name='State';
var $hasMany=array(
'MerryParent',
'MerryClass',
'City'=>array(
'className'=>'City',
'foreignKey'=>'state_id'
//'dependent'=>true
),
'Student'=>array(
'className'=>'Student',
'foreignKey'=>'state_id'
)
);
}
?>
The controllers
students_controller.php
<?php
class StudentsController extends AppController{
var $name='Students';
var $helpers = array('Html','Form','Ajax','Javascript');
var $components=array('RequestHandler');
function getcities(){
$options=$this->Student->City->find('list',
array
('conditions'=>array(
'City.state_id'=>$this->data['Student']['state_id']
),
'group'=>array('City.name')
)
);//closing parentheses for find('list'...
$this->render('/students/ajax_dropdown');
}
function add(){
if (!empty($this->data)){
/*var_dump($this->data);
die(debug($this->Student->validationErrors)); */
$student=$this->Student->saveAll($this->data,array('validate'=>'first'));
if (!empty($student))
{
$this->Session->setFlash('Your child\'s admission has been received. We will send you an email shortly.');
$this->redirect(array('controller'=>'pages', 'action'=>'home'));
}
} //for if (!empty....
$states=$this->Student->State->find('list');
$cities=array();
$this->set(compact('states','cities'));
}//end function
}
?>
merry_parents_controller.php
<?php
class MerryParentsController extends AppController{
var $name='MerryParents';
}
?>
add.ctp
<?php
echo $javascript->link('prototype',false);
echo $form->create('Student');
echo '<fieldset>';
echo '<legend>Student Information</legend>';
echo $form->input('Student.name');
$options = array('Male'=>'Male','Female'=>'Female');
$attributes = array('value'=>'Male');
echo $form->radio('Student.gender',$options,$attributes);
echo $form->input('Student.dob', array('label'=>'Date of Birth',
'dateFormat'=>'DMY',
'empty'=>true,
'timeFormat' => '',
'minYear' => (
date('Y') - 5
),
'maxYear' => (
date('Y') - 2
)
));
echo $form->input('Student.class_id',
array(
'label'=>'Enquiry Class for',
'empty'=>'Choose one',
'options'=>array('1'=>'Playgroup','2'=>'Nursery','3'=>'LKG', '4'=>'UKG')
)
);
echo '</fieldset>';
echo '<fieldset>';
echo '<legend>Parent Information</legend>';
//echo $form->input('Student.parent_id', array('type'=>'hidden'));
echo $form->input('MerryParent.initial',
array('empty'=>true,
'options'=>array('Dr'=>'Dr',
'Mr'=>'Mr',
'Mrs'=>'Mrs',
'Ms'=>'Ms')
)
);
echo $form->input('MerryParent.name', array('label'=>'Parent/Guardian Name'));
echo $form->input('MerryParent.email');
echo $form->input('MerryParent.landline');
echo $form->input('MerryParent.mobile');
echo $form->input('MerryParent.address');
echo $form->input('Student.state_id');
echo $form->input('Student.city_id');
echo $form->input('MerryParent.postal_code');
$options = array('url' => 'getcities', 'update' => 'StudentCityId');
echo $ajax->observeField('StudentStateId', $options); //observes the drop down
changes in state id and makes an xmlHttpRequest when its contents have changed.
echo '</fieldset>';
echo $form->end('Submit');
?>
ajax_dropdown.ctp
<?php foreach($options AS $k=>$v) : ?>
<option value="<?php echo $k; ?>"><?php echo $v; ?></option>
<?php endforeach; ?>
You don't need to put 0 in Model name and field, since you are saving just one student in a form. Try using Student.name instead of Student.0.name and so on for all fields. Also try to test with $validate => false if it saves then.

Resources