Im working on a site and build a custom post type. Within this custom post type I added a meta field where you can select a number from 1 trough 16. These numbers are corresponding with specific positions on my homepage. But now my issue appears. How can I query these post within these specific positions without repeating myself.
So for example...
here a post with meta value 1
here a post with meta value 2
here a post with meta value 3
ect..
Hope someone can help me a little
There are a few ways to tackle this depending on how complex your layout logic is. If you basically just need to loop through the 16 posts in order, you can use the orderby parameter in your query to sort the posts:
$args = array(
'post-type' => 'YOUR_POST_TYPE',
'orderby' => 'meta_value_num',
'meta_key' => 'YOUR_METAFIELD_SLUG'
);
$customposts = new WP_Query($args);
while( $customposts->have_posts() ): $customposts->the_post();
// loop goes here
If you need something more complex, you might be able to avoid repeating queries by querying once and then using conditionals to check the meta value. For example, with the query from the above example...
// Position 1 on your page
<div class="position-1">
<?php
while( $customposts->have_posts() ): $customposts->the_post();
$meta_value = get_post_meta(get_the_ID(), 'YOUR_METAFIELD_SLUG', true);
// Only print something here if the meta value is what we want
if( $meta_value == 1 ) :
the_title();
the_content();
endif;
// Reset the loop
$customposts->rewind_posts(); ?>
</div>
// Position 2 on your page
<div class="position-2">
<?php
while( $customposts->have_posts() ): $customposts->the_post();
$meta_value = get_post_meta(get_the_ID(), 'YOUR_METAFIELD_SLUG', true);
if( $meta_value == 2 ) :
the_title();
the_content();
endif;
// Reset the loop again
$customposts->rewind_posts(); ?>
....and so on.
You may find these helpful:
http://codex.wordpress.org/Function_Reference/get_post_meta
http://codex.wordpress.org/Class_Reference/WP_Query
Related
I am currently building some kind of video channel based on an extension I created. It consists of videos and playlist that contains videos (obviously).
I have to create a page which contains a list of videos AND playlist by category. You also can sort those items by date. Finally, the page is paginated with an infinite scrolling that should load items 21 by 21.
To do so, I created on both Video and Playlist repositories a "findByCategory" function which is really simple :
$query = $this->createQuery();
return $query->matching($query->equals('categorie.uid',$categoryUid))->execute()->toArray();
Once I requested the items I need, I merge them in one array and do my sorting stuff. Here is my controller show action :
if ($this->request->hasArgument('sort'))
$sort = $this->request->getArgument('sort');
else
$sort = 'antechrono';
//Get videos in repositories
$videos = $this->videoRepository->findByCategorie($categorie->getUid());
$playlists = $this->playlistRepository->findByCategorie($categorie->getUid());
//Merging arrays then sort it
if ($videos && $playlists)
$result = array_merge($videos, $playlists);
else if ($videos)
$result = $videos;
else if ($playlists)
$result = $playlists;
if ($sort == "chrono")
usort($result, array($this, "sortChrono"));
else if ($sort == "antechrono" || $sort == null)
{
usort($result, array($this, "sortAnteChrono"));
$sort="antechrono";
}
$this->view->assignMultiple(array('categorie' => $categorie, 'list' => $result, 'sort' => $sort));
Here is my view :
<f:widget.paginate objects="{list}" as="paginatedList" configuration="{addQueryString: 'true', addQueryStringMethod: 'GET,POST', itemsPerPage: 21}">
<div class="videos row">
<f:for each="{paginatedList}" as="element">
<f:render partial="Show/ItemCat" arguments="{item: element}"/>
</f:for>
</div>
</f:widget.paginate>
The partial render shows stuff including a picture used as a cover. So I need at least this relation in the view.
This works fine and shows only the items from the category that is requested. Unfortunatly I have a huge performance issue : I tried to show a category that contains more than 3000 records and It takes about one minute to load. It's a little bit long.
By f:debugging my list variable, I see that it contains every records even through it shouldn't be the case (that's the point of pagination...). So the first question is : is there something wrong in the way I did my pagination ?
I tried to simplify my requests by enabling the rawQuery thing ($query->execute(true)) : I get way better performance, but I can't get the link for the pictures (in my view, I get 1 or 0 but not the picture's uid...). Second question : is there a way to fix this issue ?
I hope my description is clear enough. Thanks for your help :-)
When you execute a query, it will not actually fetch the data from the database until the results are accessed. If the paginate widget gets a query result it will add limits and offset to the query and then fetch the data from the database, so you will only get the records that are shown on a page.
In your case you added toArray() after execute(), which accesses the results, so the data is fetched from the database and you get all records. The best solution I can think of is to combine the 2 tables into 1 so you can do it with a single query and don't have to merge and order them in PHP.
As long as you sort the data after the query you have to handle all data (request all records and especially resolve all relations).
Try to sort the data in the query itself (order by), so you could restrict the data to only those records which are needed for the current 'page' (limit <offset>,<number>).
Here a complex query with join and limit could be faster than a full query and filtering in PHP.
I'm using an InfiniteLoader with a Table and I have the indices of some entries that match a specific criteria stored in state. If the user would want to go through those entries, I would have to pass down to the Table each index (following a button click) and use it in scrollToIndex. However, after it's passed down, I would have to make it null again, otherwise if the user were to scroll up/down the Table, he would always end to the same index specified by scrollToIndex instead of the current scroll position.
Is there a better way to do that, other than setting the state twice, once with an index and then with a null value?
Hope my question is clear.
Table (and Grid and List) also have public methods for setting a 1-time scroll index. For Table the method is scrollToRow and you just pass it the index you want to scroll to. Maybe that would be more to your liking?
Edit: In response to the follow-up question of how you get a reference to Table if you also need to pass it to InfiniteLoader:
<InfiniteLoader {...infiniteLoaderProps}>
{({ onRowsRendered, registerChild }) => (
<Table
{...tableProps}
onRowsRendered={onRowsRendered}
ref={(ref) => {
this._tableRef = ref // Store for yourself
registerChild(ref) // And pass on to InfiniteLoader
}}
>
{/* Columns ... */}
</Table>
)}
</InfiniteLoader>
I have four fields. Lets call them a, b, c and d. I need to validate them.
Error is when:
One til three fields are not empty;
Error is not when:
All fields are not empty,
All fields are empty;
Any neat solution here? Thanks in advice.
Edit:
Only relationships are that all four variables are prefixed with event_. It gives me event_name, event_description etc..
Edit #2:
At the moment I have something like...
if (
!empty($values['event_date'])
&& !empty($values['event_time'])
&& !empty($values['event_name'])
&& !empty($values['event_description'])
) {
It checks that all fields are filled up and then, if that's true, adds event.
As I said before, I need to display user-friendly error when some field isn't filled up (for example, user had forgot to enter description). Anyway, when all fields are filled up (it means - all okay) or when no fields are filled up (it means - user ignores event adding and don't want to add one) - no error should be displayed.
I could write code with 16 'if' statements, but isn't there any better way? :)
This isn't beautiful, but as long as you have something unique about the fields you want to check (such as "event_..."), you could loop through the variable array ($values, $_POST, etc) and check only the fields that matter. Then, you can easily check for an all or none situation.
Here is a quick example:
$total = 0;
$filled = 0;
foreach($values as $field => $val) {
if(strpos($field,'event_') === 0) {
$total++;
if( ! empty($val)) {
$filled++;
}
}
}
if($filled == 0 OR $total == $filled) {
//PASS VALIDATION
} else {
//FAIL VALIDATION
}
Is there a relationship between one of the entered values and the none entered values??
could you just parse it as an empty value?
if ( ! isset($post->a) ) $post->a = '';
I've been trying to find a way of forcing an attribute to show as a dropdown rather than a block of options but had no luck. The code current looks like this:
case 'select': ?>
<div class="input-box"> <?php echo $this->getAttributeSelectElement($_attribute) ?> </div>
<?php endswitch; ?>
Does anyone know how to make this look like a dropdown list instead?
Thanks in advance
I had the same problem earlier today and the strangest thing was that I had the attributes (drop down) with the same properties but one displaying a drop down menu and the other a multi select menu in the advanced search.
I did some testing with different settings and it turned out that in the advanced search every attribute that is a list (drop down and multi-select) and it has more than 2 options is displayed as multi-select.
I had a look at Mage_CatalogSearch_Block_Advanced_Form stored in /app/code/core/Mage/CatalogSearch/Block/Advanced/Form.php and I saw where this condition of 2 is checked. The magento core team made it like this to make sure that the 'yesno' or boolean list are displayed as dropdown.
In the above mentioned file, starting from line 173 (on the current version of magento)
is the following code:
public function getAttributeSelectElement($attribute)
{
$extra = '';
$options = $attribute->getSource()->getAllOptions(false);
$name = $attribute->getAttributeCode();
// 2 - avoid yes/no selects to be multiselects
if (is_array($options) && count($options)>2) {
. . .
If you change the number two on the last line with the number 5, advanced search will display drop down menu on every attribute that has less than 6 options.
What I did for myself is I added a new method, getAttributeDropDownElement(), bellow getAttributeSelectElement() that looks like this:
public function getAttributeDropDownElement($attribute)
{
$extra = '';
$options = $attribute->getSource()->getAllOptions(false);
$name = $attribute->getAttributeCode();
// The condition check bellow is what will make sure that every
// attribute will be displayed as dropdown
if (is_array($options)) {
array_unshift($options, array('value'=>'', 'label'=>Mage::helper('catalogsearch')->__('All')));
}
return $this->_getSelectBlock()
->setName($name)
->setId($attribute->getAttributeCode())
->setTitle($this->getAttributeLabel($attribute))
->setExtraParams($extra)
->setValue($this->getAttributeValue($attribute))
->setOptions($options)
->setClass('multiselect')
->getHtml();
}
The next thing you need to do is a small if statement within the switch of the form (see bellow) that will check the name of the attribute and base on that to call either getAttributeSelectElement() or our new method getAttributeDropDownElement(). I leave this job to you :)
case 'select': ?>
<div class="input-box"> <?php echo $this->getAttributeSelectElement($_attribute) ?> </div>
<?php endswitch; ?>
Sorry for my English...i'm french ;-)
In your admin panel, you can choose the type of your Attributes
Make sure that your attribute is declared as a list. In my Magento version, it's the third information in the attribute admin panel after code and scope.
PoyPoy
Magento has a class for generating selects available as a Mage_Core_Block_Html_Select class (/app/code/core/Mage/Core/Block/Html/Select.php).
On your design template directory template/catalogsearch/advanced/form.phtml, replace
echo $this->getAttributeSelectElement($_attribute);
With
echo $this->getLayout()->createBlock('core/html_select')
->setOptions( $_attribute->getSource()->getAllOptions(true))
->setName($_attribute->getAttributeCode())
->setClass('select')
->setId($_attribute->getAttributeCode())
->setTitle($this->getAttributeLabel($_attribute))
->getHtml();
I have a custom list in SharePoint (specifically, MOSS 2007.) One field is a yes/no checkbox titled "Any defects?" Another field is "Closed by" and names the person who has closed the ticket.
If there are no defects then I want the ticket to be auto-closed. If there are, then the "Closed by" field ought to be filled in later on.
I figured I could set a calculated default value for "Closed by" like this:
=IF([Any defects?],"",[Me])
but SharePoint complains I have referenced a field. I suppose this makes sense; the default values fire when the new list item is first opened for entry and there are no values in any fields yet.
I understand it is possible to make a calculated field based on a column value but in that case the field cannot be edited later.
Does anyone have any advice how to achieve what I am trying to do?
Is it possible to have a "OnSubmit" type event that allows me to execute some code at the point the list item is saved?
Thank you.
Include a content editor web part in the page (newform.aspx / editform.aspx) and use jQuery (or just plain javascript) to handle the setting of default values.
Edit: some example code:
In the lists newform.aspx, include a reference to jquery. If you look at the html code, you can see that each input tag gets an id based on the field's GUID, and a title that's set to the fields display name.
now, using jquery we can get at these fields using the jQuery selector like this:
By title:
$("input[title='DISPLAYNAMEOFFIELD']");
by id (if you know the field's internal guid, the dashes will ahve to be replaced by underscores:
// example field id, notice the guid and the underscores in the guid ctl00_m_g_054db6a0_0028_412d_bdc1_f2522ac3922e_ctl00_ctl04_ctl15_ctl00_ctl00_ctl04_ctl00_ctl00_TextField
$("input[id*='GUID']"); //this will get all input elements of which the id contains the specified GUID, i.e. 1 element
We wrap this in the ready() function of jQuery, so all calls will only be made when the document has fully loaded:
$(document).ready(function(){
// enter code here, will be executed immediately after page has been loaded
});
By combining these 2 we could now set your dropdown's onchange event to the following
$(document).ready(function(){
$("input[title='DISPLAYNAMEOFFIELD']").change(function()
{
//do something to other field here
});
});
The Use jQuery to Set A Text Field’s Value on a SharePoint Form article on EndUserSharePoint.com shows you how to set a default value for a field using JavaScript/jQuery.
They also have a whole series of articles on 'taming calculated columns' that will show you many more powerful options you have for calculated fields with the use of jQuery.
One thing to be aware of when inserting JavaScript into a SharePoint page and modifying the DOM is support. There is a small chance that a future service pack will break the functionality you add, and it is quite likely that the next version of SharePoint will break it. Keeping this mind however, I believe it's a good solution at this time.
I've got a walk through with sample code that may help
Setting a default duration for new calendar events
It sets the End Time/Date fields to Start Time + 1.5 hours when you create a new event.
Its complicated a little by the steps need to do the time/date work, but you'll see examples of how to find the elements on the form and also one way to get your script onto the newform.aspx without using SPD.
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js">
</script>
<script type="text/javascript">
// Set the hours to add - can be over 24
var hoursToAdd = 1;
// Mins must be 0 or div by 5, e.g. 0, 5, 10, 15 ...
var minutesToAdd = 30;
// JavaScript assumes dates in US format (MM/DD/YYYY)
// Set to true to use dates in format DD/MM/YYYY
var bUseDDMMYYYYformat = false;
$(function() {
// Find the start and end time/minutes dropdowns by first finding the
// labels then using the for attribute to find the id's
// NOTE - You will have to change this if your form uses non-standard
// labels and/or non-english language packs
var cboStartHours = $("#" + $("label:contains('Start Time Hours')").attr("for"));
var cboEndHours = $("#" + $("label:contains('End Time Hours')").attr("for"));
var cboEndMinutes = $("#" + $("label:contains('End Time Minutes')").attr("for"));
// Set Hour
var endHour = cboStartHours.attr("selectedIndex") + hoursToAdd;
cboEndHours.attr("selectedIndex",endHour % 24);
// If we have gone over the end of a day then change date
if ((endHour / 24)>=1)
{
var txtEndDate = $("input[title='End Time']");
var dtEndDate = dtParseDate(txtEndDate.val());
if (!isNaN(dtEndDate))
{
dtEndDate.setDate( dtEndDate.getDate() + (endHour / 24));
txtEndDate.val(formatDate(dtEndDate));
}
}
// Setting minutes is easy!
cboEndMinutes.val(minutesToAdd);
});
// Some utility functions for parsing and formatting - could use a library
// such as www.datejs.com instead of this
function dtParseDate(sDate)
{
if (bUseDDMMYYYYformat)
{
var A = sDate.split(/[\\\/]/);
A = [A[1],A[0],A[2]];
return new Date(A.join('/'));
}
else
return new Date(sDate);
}
function formatDate(dtDate)
{
if (bUseDDMMYYYYformat)
return dtDate.getDate() + "/" + dtDate.getMonth()+1 + "/" + dtDate.getFullYear();
else
return dtDate.getMonth()+1 + "/" + dtDate.getDate() + "/" + dtDate.getFullYear();
}
</script>