First of all, sorry for this long subject, but I didn't find a way to explain the issue otherwise...
I'm facing an issue with shortcode executed within get_the_content() and with the management of infinite nested loop that can occur.
My plugin is used as a shortcode to display posts of a specific list of categories passed as argument.
The shortcode can be used as well on a page, on a post or on a widget.
It can be summarized like this:
Root Post/Page/Widget body with [shortcode category_slug_list="category_slug1"]
=> post 1 of category with slug1 is displayed
post 2 of category with slug1 is displayed
...
...
post n of category with slug1 is displayed
Each post is displayed in the loop with:
$content = get_the_content();
$content = apply_filters('the_content', $content);
If it's used on a post, the main problem here is that the post MUST NOT belong to one of the categories specified by the shortcode to avoid infinite nested loop.
When the post that belong to one of the categories is at first level of depth, the management of this is done with:
if (is_single()) {
if (in_category($category_slug_array)) {
// The function must not proceed with this post !!!
$out = '<div>To proceed inside a post, the post MUST not belong to one of the categories asked by "category_slug_list" option, to avoid infinite nested loop...</div>';
return $out;
}
}
It works because (is_single) is related to the first level of depth and here the first level is a post.
But, for example, when the first level of depth is a page, when one of the posts displayed has its own shortcode there is a problem.
As the content of the post is retrieved with get_the_content(), when the shortcode in it is executed, I can't find a way to retrieve the context, as is_single() returns false because it's related to the root page, and not the current post from which we got the value of get_the_content().
Example:
Root Page body with [shortcode category_slug_list="category_slug2"]
=> post 1 of category with slug2 displayed
post 2 of category with slug2 displayed
post 2 body with [shortcode category_slug_list="category_slug2"]
...
...
post n of category with slug2 displayed
From this example, is there a way to get the "post 2" context, I mean verify that it's a post, and then check the in_category function on it?
Any clue, Idea, Advice will be welcomed...
So, I've finally found a solution to this:
The only way I've found was to be able to pass the post id as attribute to the nested shortcode.
In the shortcode function, in the posts loop, I use str_ireplace in the post content to add attribute if the shortcode is found:
$content = str_ireplace("[shortcode-function-name",'[shortcode-function-name post_id="'.$post->ID.'"',$content);
So, at the beginning of the shortcode function, I retreive the post_id attribute:
extract(shortcode_atts(array(
"post_id" => '',
"other_attribute" => 'default_value',
...
...
"last_attribute" => 'default_value'
), $atts));
And then, in addition to the first check in the case of root post, I can now check with the post ID:
if ($post_id) {
if (in_category($category_slug_array,$post_id)) {
// The function must not proceed with this post !!!
$out = '<div>To proceed inside a post, the post MUST not belong to one of the categories asked by "category_slug_list" option, to avoid infinite nested loop...</div>';
return $out;
}
}
In case it can help someone who try to work with a shortcode function inside a nested loop depending on post specificities...
Related
I wanted to know how can I count the number of times a category was chosen in laravel.
Here is what I have in my code so far
<ul>
<li>Financial Services()</li>
<li>Beauty & Personal Care()</li>
<li>Health & Medical()</li>
<li>Fitness & Wellness Services()</li>
</ul>
Thanks in advance
Based on the piece of code you've provided, I'm assuming that you want it to count each time someone clicks the category. For this, the best method I could think of is adding a field within the categories table which is updated every time someone clicks a link.
Do remember to lock your database using laravel's lockForUpdate() method (read more about that here) when reading / writing to this field, since otherwise multiple people could read the same even though one of them should've read the updated value.
But basically here's what I'd do:
CategoryController:
public function search(Request $request) {
...validation code
//Find the category you want
$category = Category::where('name', $request->only(['what']))->first();
//Add one to this category's click count
$category->update([
'clicks' => $category->clicks + 1
]);
...followup code / redirect
}
I have two webpages A and B, A has a link button like this
Link
and B is a page about article lists, and has a table with column [#, books_name, author......]
And also, B has a query table to query data, user can input books_name or author_name, then it'll show the data user wants.
My question is, how to modify link in A Page and let it send autocomplete parameter to B?
The result of B's url may be like this
https://{B's path}/?q_author_name={the parameter from Page A}
Hope someone can help me. Thanks!
You can pass the query parameter as second argument to route()
{{ route('to_B', ['q_author_name' => 'Author']) }}
would generate
https://{B's path}?q_author_name=Author
provided you don't have any route parameters in your route definition like
Route::get("{B's path}/{param1}", function () {})->name('to_B');
in that case the first key from the route array would be passed to the param1 making your route as
https://{B's path}/Author
I have a number of routes on my website that use a custom pagination solution, like /categories/{category}/page/{page?}. I currently have each controller set like the following:
public function index($category, $page = null) {
if(!isset($page) || !is_numeric($page) || $page < 1) {
return redirect()->route('parent', ['category' => $category]);
}
}
This has the desired effect of checking if the provided page number exists, is, indeed, a number, and if it is greater than zero. In the event that any of those conditions are false, it redirects to the parent route, /categories/{category}.
The problem is, for each of these solutions, they all require me to write in the name of the parent route to do the redirect. If I want to just cut off the /page/ and onwards, but keep any get variables and post variables, how would I do that?
If you are trying to redirect back to the previous page with the inputs, use the following code.
return redirect()->back()->withInput();
In the above case, it will work only if you are coming to the url /categories/{category}/page/{page?} from /categories/{category}
I get a list of magento ids from a web service. I load these into and array $product_ids, so I have something like this:
Array
(
[0] => 1965
[1] => 3371
[2] => 1052
)
I can then make this into a collection:
$collection = Mage::getModel('catalog/product')->getCollection()
->addIdFilter($product_ids);
Using my Magento inspector, I've seen that the category pages use the class Mage_Catalog_Block_Product_List to display lists of products. I'd like to do something similar in my class. I've tried loading:
$ProductList = new Mage_Catalog_Block_Product_List();
$ProductList->setCollection($collection);
And then I've tried to load the HTML of the results as follows:
$CollectionHTML = $ProductList->_toHtml();
But $CollectionHTML is empty.
How would I get the HTML of what you see in the list view (i.e. the generated output of frontend/base/default/template/catalog/product/list.phtml, but given my collection)?
Making the code work the right way is much more easier in Magento than trying to work with ugly legacy code. I would gladly help you make the code the proper way when you have specific questions. Also, in the longterm, technical debt is gonna cost alot more.
Anyway, back to your issue.
In Magento block are not instantiated like in any app $myvar = new className ... almost never. This tutorial can help you understand better Magento's layout and blocks.
But if you want to create a block a way to do it is:
$block = Mage::getSingleton('core/layout')->createBlock('catalog/product_list')
Now related to your product collection you should check how Mage_Catalog_Block_Product_List::_getProductCollection actually works, because it uses the layered navigation, not a simple product collection.
Further, assuming that at least you are using a Magento controller and you are within a function, the following code will display the first page of products for a specified category:
//$category_id needs to be set
$layout = Mage::getSingleton('core/layout');
$toolbar = $layout->createBlock('catalog/product_list_toolbar');
$block = $layout->createBlock('catalog/product_list');
$block->setChild('toolbar', $toolbar);
$block->setCategoryId($category_id);
$block->setTemplate('catalog/product/list.phtml');
$collection = $block->getLoadedProductCollection();
$toolbar->setCollection($collection);
//render block object
echo $block->renderView();
Displaying specific ids:
you use root category id for $category_id variable (also make sure that display root category is set (or another category id that contains your product ids)
you can hook into catalog_block_product_list_collection event to add your ID Filter to the collection (this is called in _beforeToHtml function)
But, all this construction is not solid and there are still some points that require attention (other child blocks, filters and so on)
I have two models: Plans and PlanDetails.
Relationship is: PlanDetails hasMany Plans. Plans belongTo PlanDetails.
In the PlanDetails view.ctp, I am pulling in related Plans.
I am trying to sort the Plans by ANY field (I've tried them all), and I cannot get it working. I assume I am overlooking something very simple.
Here is my base code:
PlanDetail >> view.ctp:
...foreach ($planDetail['Plan'] as $plan_edit) :
$class = null;
if ($i++ % 2 == 0) {
$class = ' class="altrow"';
}...
<?php echo $this->Paginator->sort('Plan ID', 'Plan.id'); ?>...
...<?php echo $plan_edit['id']; ?>
plan_details_controller.php:
...function view($id = null) {
if (!$id) {
$this->Session->setFlash(__('Invalid plan detail', true));
$this->redirect(array('action' => 'index'));
}
$this->PlanDetail->recursive = 2; // To run the editable form deeper.
$this->set('planDetail', $this->PlanDetail->read(null, $id));
$this->set('plan', $this->paginate('Plan'));
}...
I should add, no errors are being thrown and the sort() arrows on the ID field are showing as expected, but the sort order DOES not change when clicked either way.
Sorry, I'm not able to comment on the question itself, but I've noticed that in your action, you set planDetail to be the PlanDetail record you read (with recursive set to 2), and then you set plan to be the result of the paginate call.
Then, in your view template, you're iterating over $planDetail's contained Plan association, like this:
foreach ($planDetail['Plan'] as $plan_edit):
But in order to get the sorting and pagination done, you need to be displaying the results of the paginate call i.e. iterate over the records contained in $plan.
Do a debug($plan) in your view template to see what results you get there and to see if the records' ordering changes when you sort by different fields.
Also, perhaps you're using syntax I'm not aware of, but if you simply call $this->paginate('Plan') in your controller, I don't know that you're going to get only the related Plan records for your particular PlanDetail record. (There's nothing tying the $id passed into your view action with the Plan records.) You might need to add some conditions to the paginate call, like so:
$this->paginate['Plan'] = array('conditions' => array('Plan.plan_detail_id' => $id));
$this->set('plans', $this->paginate('Plan'));
Here is what I did to solve this. Based on some helpful direction from johnp & tokes.
plan_details/view.ctp:
...$i = 0;
foreach ($plan as $plan_edit) : // note $plan here.
}...
In my plan_details_controller.php view action:
$conditions = array("Plan.plan_detail_id" => "$id");
$this->set('plan', $this->paginate('Plan', $conditions)); // note "plan" in the first argument of set (this is required to pass the results back to the view). Also. The $condition is set to return ONLY plans that match the plan_detail_id of id (on the plan_details table).
And in my view, in order to get my results (because I changed the array name), I had to change the way I was getting the values to:
$plan_edit['Plan']['modified'] // note I placed ['Plan'] in front of these calls as the array called for this to get the data...
Well until the next problem! SOLVED.