vqmod advanced search by searching twice - vqmod

I am faced with a problem where I need to search for a line of code which is too common.
Say I have this code and need to convert it into an array_merge:
$this['data'][] = array(
'firstname' => $query->row['firstname'],
'lastname' => $query->row['lastname'],
'company' => $query->row['company'],
'company_id' => $query->row['company_id']
);
vqmod
<operation>
<search position="replace"><![CDATA[
$this['data'][] = array( ]]></search>
<add><![CDATA[
$this['data'][] = array_merge($data, array( ]]></add>
</operation>
<operation>
<search position="replace"><![CDATA[
); ]]></search>
<add><![CDATA[
)); ]]></add>
</operation>
Problem with this is that it's trying to search for code which is too common.
I could use offset to replace the whole thing because we use other extensions modifying this same array. Also can't rely that company_id is always last.
So instead I was thinking if there was a way to search twice or something similar to this concept:
Search for: $this['data'][] = array( find the line number of this.
Then start the next search from this line number, finding the next occurrence of: );
This same idea could then be applied to a method where I wanted to add some logic before the returned data.
<operation>
<search position="before"><![CDATA[
private static function _cacheName ]]></search>
<search2><![CDATA[
return ]]></search2>
<add><![CDATA[
// custom code ]]></add>
</operation>

You can use index. As stated in the vqmod's Scripting manual, you can specify which results you'd like to use. So the code below would find the 2nd occurrence of the text and replace it with the specified part:
<operation>
<search position="replace" index="2"><![CDATA[
$this['data'][] = array( ]]></search>
<add><![CDATA[
$this['data'][] = array_merge($data, array(]]></add>
</operation>
You can apply the same thing to the closing );. Alternatively, you could try to figure out how many lines are within that array, and you could use an offset with replace:
<operation>
<search position="replace" index="2" offset="4"><![CDATA[
$this['data'][] = array( ]]></search>
<add><![CDATA[
));]]></add>
</operation>
A good starting point would be to check the file which is modified, in the vqcache folder, so you would have an idea on how many lines shall you offset.
It's important to note, that vqmod can only search for a single line or a part of a single line.

Related

Making Column Grid Filter Accept Complex Value In Magento

In Magento, there's a column in Product Grid/Table which is Name.
It only accepts or filter the table if you put the exact word/s.
Sample is "What Happened Last Night" (just an example)
If you put "What" it will filter correctly, if you add "Happened" it will still filter correctly but if you input "What Last" it will return 0 records unless there's a "What Last" record.
I have this code in the core itself.
$this->addColumn('name',
array(
'header'=> Mage::helper('catalog')->__('Name'),
'index' => 'name',
'filter_condition_callback' => array($this, '_customNameFilter'),
));
$store = $this->_getStore();
if ($store->getId()) {
$this->addColumn('name_search', array(
'header'=> Mage::helper('catalog')->__('Name in %s', $store->getName()),
'type' => 'text',
'filter_condition_callback' => array($this, '_customNameFilter'),
));
}
Based on this link, Grid filter for columns with complex values
I need to remove the index and adjust something or some files in which I'm totally lost as the author did not mention the file paths used.
Can anyone guide me on what file should I adjust or create if needed.
The Code above has the file path of
app\code\core\Mage\Adminhtml\Block\Catalog\Product\Grid.php
What other files/modules should I adjust an what lines of code should I'd be looking at to attain this result.
I could not thank you enough for assisting me.
here is a working solution for your case
protected function _customNameFilter($collection, $column)
{
if (!$value = $column->getFilter()->getValue()) {
return $this;
} else if (preg_match('/\s+/', $value)) {
$tokens = explode(' ', $value);
foreach($tokens as $token) {
$this->getCollection()->addAttributeToFilter($column->getData('index'), array('like' => '%'.$token.'%'));
}
} else {
$this->getCollection()->addAttributeToFilter($column->getData('index'), array('like' => '%'.$value.'%'));
}
return $this;
}
this space tokenizer filter will also work with any other grid field.
Atwix example will not solve the problem you described. If you want to make a filter smart enough to be on-the-fly tokenizer you will have to split the search string by spaces and add each token to where part of the query with "OR LIKE %'.$token.'%"
Solution
you will have to rewrite Mage_Adminhtml_Block_Catalog_Product_Grid in a local Module with following xml:
/app/code/local/Your/Module/etc/config.xml
<global>
<blocks>
<adminhtml>
<rewrite>
<catalog_product_grid>Your_Module_Block_Adminhtml_Catalog_Product_Grid</catalog_product_grid>
</rewrite>
</adminhtml>
</blocks>
</global>
and add following methods into class
/app/code/local/Your/Module/Block/Adminhtml/Catalog/Product/Grid.php
class Your_Module_Block_Adminhtml_Catalog_Product_Grid extends Mage_Adminhtml_Block_Catalog_Product_Grid {
protected function _prepareColumns()
{
$this->addColumn('name_search', array(
'header'=> Mage::helper('catalog')->__('Name in %s', $store->getName()),
'type' => 'text',
'filter_condition_callback' => array($this, '_customNameFilter'),
));
return parent::_prepareColumns();
}
protected function _customNameFilter($collection, $column)
{
if (!$value = $column->getFilter()->getValue()) {
return $this;
}
$tokens = explode(' ', $value);
$where = "LIKE '%".$value."%'";
foreach($tokens as $token) {
$where .= " OR LIKE '%".$token."%'";
}
$this->getCollection()->getSelect()->where(
"(at_name.value ?)"
, $where);
return $this;
}
}
And dont forget to rename Your_Module with your own namespace and add module description xml to /app/etc/modules/
you're welcome

Seeding validation array

I am creating a lot of questions via a seeder. The format I am doing is this
DB::table('questions')->insert([
'name' => 'questionOne',
'rule' => 'nullable|max:50|regex:/(?=.)^\£?(([1-9][0-9]{0,2}(,[0-9]{3})*)|[0-9]+)?(\.[0-9]{1,2})?$/'
]);
Whereby I provide a field name and a validation rule. I was noticing that when applying the above rule, the validation would fail, stating
preg_match(): No ending delimiter '/' found
I done some research and found out that
When using the regex pattern, it may be necessary to specify rules in
an array instead of using pipe delimiters, especially if the regular
expression contains a pipe character.
As recommended, I changed my seeder to this
DB::table('questions')->insert([
'name' => 'questionOne',
'rule' => ['nullable|max:50|regex:/(?=.)^\£?(([1-9][0-9]{0,2}(,[0-9]{3})*)|[0-9]+)?(\.[0-9]{1,2})?$/']
]);
However, when I try to seed with the above, I get an Array to String conversion error. The way I am applying the validation is like so
$rules = [];
$questions = Question::all();
foreach ($questions as $question) {
if (!empty($question->rule)) {
$rules["questions.{$question->id}"] = $question->rule;
}
}
$this->validate($request, $rules);
Is there any way I can get the above regex to work? One point to note is that only a few questions have this regex, if that matters?
Thanks
When using the regex pattern, it may be necessary to specify rules in an array instead of using pipe delimiters, especially if the regular expression contains a pipe character.
This is referring to the $rules variable passed into $this->validate; your regex pattern contains a pipe character | which interferes with Laravel's ability to split up the rule string into an array internally.
Storing the rules in string format with pipe separators also makes it difficult for you to split them into an array upon retrieval from the DB. I'd recommend storing them as a similarly delineated structure like JSON, which would make your seeder:
DB::table('questions')->insert([
'name' => 'questionOne',
'rule' => json_encode([
'nullable',
'max:50',
'regex:/(?=.)^\£?(([1-9][0-9]{0,2}(,[0-9]{3})*)|[0-9]+)?(\.[0-9]{1,2})?$/'
])
]);
and validation:
$rules = [];
$questions = Question::all();
foreach ($questions as $question) {
if (!empty($question->rule)) {
$rules["questions.{$question->id}"] = json_decode($question->rule, true);
}
}
$this->validate($request, $rules);
You would also want to change the rule column type to JSON in your questions table migration.
To further simplify the code, you could make use of Laravel's attribute casting feature which claims to handle the json_encode/json_decode for you:
protected $casts = [
'rule' => 'array',
];

Ajax dont return values in drupal 7

Im in drupal 7, i need to make a select with many options, depends of the option taken, in a textarea will be loaded several values in a string.
After hours of test i come here for help.
Im working on a basic page:
function ajax_load_all_admins($form, &$form_state) {
$form = array();
$form['changethis'] = array(
'#type' => 'select',
'#options' => array(
'' => '',
'1' => 'Cargar todos los admins'
),
'#ajax' => array(
'event' => 'change',
'callback' => 'ajax_load_all_admins_callback',
'wrapper' => 'listaCorreos-div'
)
);
$form['listaCorreos'] = array(
'#type' => 'textarea',
'#prefix' => '<div id="listaCorreos-div">',
'#suffix' => '</div>'
);
if (!empty($form_state['values']['changethis'])) {
$payments_list = db_query('QUERY WORKING WELL');
$value = '';
foreach ($payments_list as $payment) {
$value .= $payment->admin . ',';
}
trim($value, ',');
$form['listaCorreos']['#default_value'] = $value;
}
return $form;
}
function ajax_load_all_admins_callback($form, $form_state) {
return $form['listaCorreos'];
}
$form = drupal_get_form('ajax_load_all_admins');
print drupal_render($form);
The Ajax call is working but i only recibe:
0: {command:settings, settings:{basePath:/, pathPrefix:,…}, merge:true}
No other one position.
I think it can be for the drupal_render, but dont know why?
Thanks in advice.
since the ajax itself works;
Looks to me like the db_query isn't working well and/or returning unexpected results.
My advice: You should be able to debug. i.e. setting a breakpoint and stepping into your code line by line
I do it with netbeans & XDEBUG
This should gives you a great edge solving this & upcoming similar problems, as you'll be able to monitor your variables & the execution tree of your code
Best of luck.
--edit-- This should be totally in the comment section, but ... new here , cant comment at the moment.. apologies.
I was doing this ajax functions in a simple view, created to make an specific form.
I move all the logic to a new module, instead of simple view and now is working.
I take a look to the ajax examples of "examples module" and test if they works on a simple view,like my code, dont works.
I think for any reason, drupal ajax only works if the render is not manually, like i was doing.
Thanks.

List node comments using existing comment.tpl.php from custom Drupal module

I have created a module which gets the comments from the nodes which the user has specified as 'favourites'. So I'm not trying to output all comments from all nodes like the recent comments block do, but just the ones from nodes specified as 'favourites'.
The queries all work, I've tested this by printing values from the different objects. So I've got the whole comment object for each comment and the corresponding node object. I've been able to create lists of the cid, nid, comment text etc. and output these with
$block['content'] = theme('item_list', array('items' => $items));
but how would I go about rendering the comment objects I've got in my module in the same layout/design as I have on my node pages? The comments on my node pages are rendered with the comment.tpl.php file which I set up with my own layout/design and I'd like my module to render these comments the same way.
So this is my hook_block_view() implementation which I believe is the correct way for output from a module:
function jf_comment_feed_block_view($delta = '') {
switch($delta){
case 'jf_comment_feed':
$block['subject'] = t('Comment feed');
if(user_access('access content')){
// Get users favourite locations
$loc_result = jf_comment_feed_locations();
$fav_loc = array();
foreach ($loc_result as $loc) {
$fav_loc[] = array(
'data' => $loc->nid,
);
}
if (empty($fav_loc)) { //No content in the last week.
$block['content'] = t('No favourite locations added.
To see what goes on at your favourite locations add locations to
+My Locations and the posts from those locations will show here.');
} else {
//Use our custom function to retrieve data.
$result = jf_comment_feed_contents($fav_loc);
// ############################################
// Here I need to create my output... I think...
// Previously I rendered my results from the query
// by using this code (this prints the comment id):
// $items = array();
// foreach ($result as $comment){
// $items[] = array(
// 'data' => comment_load($comment->cid),
// );
// }
// ############################################
if (empty($items)) { //No content in the last week.
$block['content'] = t('No posts from last week.');
} else {
// This is the code used to render the
// comment id to the block:
// $block['content'] = theme('item_list', array('items' => $items));
}
}
}
}
return $block;
}
I've also tried with:
$block['content'] = theme('comment_view', $mycomment, $mynode);
$block['content'] = theme('comment', $mycomment, $mynode);
where $mycomment is the comment object and $mynode is the node object. But this breaks the page.
Surely there must be a line of code I'm missing here, but I've now spent two days googling this and had no luck... So thanks for any help with this.
EDIT
#Clive did trigger some ideas and I tried creating my own array based on what the arrays look like on the node page. I got the structure and names for the array with the Devel Themer info module.
This array outputs the comments creators user pic and the date, but I've added a custom field, field_jf_comment, to my comments and this isn't showing, although I can see the information in the array with Devel. I don't use the standard out-of-the-box comment field because I wanted a textfield and not a scalable textarea for the input. A design decision.
Now obviously this isn't ideal as I set most of the values manually. This works for my current project, but would be cool if the module was a bit more generic so other people could use it too. When I click on a individual comment on my node page with Devel Themer info I get an array which has elements, the user object and array items such as db_is_active, is_admin among other things. If I could somehow recreate this array and then set this array to $block['content'] I believe this would work.
Here's the implementation of the array:
foreach ($result as $comment) {
$items[] = array(
'#entity_type' => 'comment',
'#bundle' => 'comment_node_location',
'#theme' => 'comment__node_location',
'#comment' => comment_load($comment->cid, FALSE),
'#node' => node_load($comment->nid),
'#view_mode' => 'full',
'field_jf_comment' => array(
'#theme' => 'field',
'#title' => 'Title',
'#access' => TRUE,
'#label_display' => 'above',
'#view_mode' => 'full',
'#language' => 'und',
'#field_name' => 'field_jf_comment',
'#field_type' => 'text',
'#entity_type' => 'comment',
'#bundle' => 'comment_node_location',
'#items' => array(
'0' => array(
// This isn't working and gives an error saying:
// Notice: Undefined property: stdClass::$field_jf_comment in
// jf_comment_feed_block_view()
'value' => $comment->field_jf_comment['und']['0']['value'],
'format' => $comment->field_jf_comment['und']['0']['format'],
'safe_value' => $comment->field_jf_comment['und']['0']['safe_value']
)
)
)
);
}
And I get it rendered with:
$block['content'] = $items;
EDIT
#Clive was right. His code does the same as mine, but in way less code. And with some modifications I managed to get my custom field in there too:
$content = '';
foreach ($items as $item) {
$single_comment = comment_load($item['cid']);
$custom_field = field_attach_view('comment', $single_comment, 'field_jf_comment');
$to_render = array(
'#theme' => 'comment',
'#comment' => $single_comment,
'#node' => node_load($item['nid']),
'field_jf_comment' => $custom_field
);
$content .= render($to_render);
}
$block['content'] = $content;
Now the only thing I'm missing is the links for each comment. The only one I'm using is the Reply to comment. Anyone got any idea of how to get that to show too?
The theme() calls probably break because you're using Drupal 7 but trying to pass the parameters in a Drupal 6 style. If you have a look at the theme_comment() documentation you can see it takes a single $variables parameter which should be an array. Try this:
$content = '';
foreach ($items as $item) {
$to_render = array(
'#theme' => 'comment',
'#comment' => comment_load($item['cid'])
);
$content .= render($to_render);
}
$block['content'] = $content;
The new Drupal 7 theme() syntax takes an array for its second argument. This array will be extracted before calling the template file so each key will become a new php var.
For example array( 'comment' => $mycomment ) will get you a $commentvariable in your template.
Hope this can help.

Magento registration with Billing & Shipping address

Is it possible that when user register in Magento, that time he also saves his Addresses (Billing & Shipping)
Create a module with a controller that extends the Mage_Customer_AccountController, containing createPostAction(). I duplicated the bit that handles the billing address, find this if-block:
if ($this->getRequest()->getPost('create_address')) {
And add this to the end of it:
if ($this->getRequest()->getPost('create_shipping_address')) {
$shippingAddress = Mage::getModel('customer/address');
$shippingAddressForm = Mage::getModel('customer/form');
$shippingAddressForm->setFormCode('customer_register_address')
->setEntity($shippingAddress);
$shippingAddressData = array(
'firstname' => $addressData['firstname'],
'lastname' => $addressData['lastname'],
'company' => $this->getRequest()->getPost('shipping_company'),
'street' => $this->getRequest()->getPost('shipping_street'),
'city' => $this->getRequest()->getPost('shipping_city'),
'country_id' => $this->getRequest()->getPost('shipping_country_id'),
'region' => $this->getRequest()->getPost('shipping_region'),
'region_id' => $this->getRequest()->getPost('shipping_region_id'),
'postcode' => $this->getRequest()->getPost('shipping_postcode'),
'telephone' => $this->getRequest()->getPost('shipping_telephone'),
'fax' => $this->getRequest()->getPost('shipping_fax')
);
$shippingAddressErrors = $addressForm->validateData($shippingAddressData);
if ($shippingAddressErrors === true) {
$shippingAddress->setId(null)
->setIsDefaultBilling($this->getRequest()->getParam('shipping_default_billing', false))
->setIsDefaultShipping($this->getRequest()->getParam('shipping_default_shipping', false));
$shippingAddressForm->compactData($shippingAddressData);
$customer->addAddress($shippingAddress);
$shippingAddressErrors = $shippingAddress->validate();
if (is_array($shippingAddressErrors)) {
$errors = array_merge($errors, $shippingAddressErrors);
}
} else {
$errors = array_merge($errors, $shippingAddressErrors);
}}
Of course you also need to duplicate the form in your themes template/customer/form/register.html, specifically the code inside this if-block:
if($this->getShowAddressFields()): ?>
Prefix all the field names IDs and in the copied code with shipping_. In the JavaScript at the bottom you need to duplicate the RegionUpdater line, like so:
new RegionUpdater('country', 'region', 'region_id', <?php echo $this->helper('directory')->getRegionJson() ?>, undefined, 'zip');
new RegionUpdater('country', 'shipping_region', 'shipping_region_id', <?php echo $this->helper('directory')->getRegionJson() ?>, undefined, 'zip');
(almost) complete code can be found here:
AccountController.php:
http://pastebin.com/9h9HqYAa
register.html:
http://pastebin.com/Q7EawU7L
It works perfectly
There is a way you can have one address input while registration.
Go to : template/customer/form/register.phtml and if($this->getShowAddressFields())
Just forcefully alter this condition and you will get address fields there.
To add to Massi and Steve's answer, in Magento 1.9.0.1, I am just learning but I was able to get this to work by adding that code to the end of _getErrorsOnCustomerAddress function in an extension of Mage_Customer_AccountController.
Not in the default magento version. you must search for an extension for this or write your own "observer" to add an address on the same time of the registration.
Like butcher said,adding that code to the end of _getErrorsOnCustomerAddress. I just tried it, and it works fine for me.
And I also set the value for "1" instead of "0" in the code
<input type="hidden" name="create_shipping_address" value="0" />
(look the link that Steve and Massi have given register.html: http://pastebin.com/Q7EawU7L)
Im on Magento 1.9.0.1

Resources