split custom archives function into columns wordpress - arrays

WordPress, you infuriate me sometimes!
What I've been trying to do for the past few hours is trying to have a way that I can display the Archive's down the sidebar with a custom date format (without changing the global options in settings), a terribly convoluted task that should really be as simple as using the_date();. I just want to make January 2013 for example, Jan 13 instead while also having one year down one column and the other in the other.
I've managed to do the custom date formatting alright by adapting this code: http://www.wpbeginner.com/wp-themes/how-to-customize-the-display-of-wordpress-archives-in-your-sidebar/ to this:
function sidebar_archive_list($arclistno = "24") {
global $wpdb;
$limit = 0;
$year_prev = null;
$months = $wpdb->get_results("SELECT DISTINCT MONTH( post_date ) AS month , YEAR( post_date ) AS year, COUNT( id ) as post_count FROM $wpdb->posts WHERE post_status = 'publish' and post_date <= now( ) and post_type = 'post' GROUP BY month , year ORDER BY post_date DESC");
foreach($months as $month) :
$year_current = $month->year;
if ($year_current != $year_prev){
if ($year_prev != null){ }
}
?>
<?php echo date("M y", mktime(0,0,0,$month->month,1,$month->year)); ?>
<?php
$year_prev = $year_current;
if(++$limit >= $arclistno) { break; }
endforeach;
}
That's worked no problems.
The trouble has now come when I've tried to split the returned content from this function into the two separate column divs. I believe I need to make the function into an array like you can get from wp_get_archives('echo=0'); (and some extra code) but my PHP skills aren't superb and I can't quite figure out how to go about recreating the same kind of thing. I think it should look something like:
array(3) { [0]=> string(86) "January 2013"
[1]=> string(90) "September 2012"
[2]=> string(82) "March 2012"
}
When var_dumped. Also it's worth bearing in mind for whatever reason the anchor links also surround the dates, but echo, rather than print out.
The code I'm using for the content split is:
<?php
$links = print(sidebar_archive_list());
$archive_n = count($links);
for ($i=0;$i<$archive_n;$i++):
if ($i<$archive_n/2):
$archive_left = $archive_left.'<li>'.$links[$i].'</li>';
elseif ($i>=$archive_n/2):
$archive_right = $archive_right.'<li>'.$links[$i].'</li>';
endif;
endfor;
?>
<div class="group">
<ul class="sb_double_column_list alignleft">
<?php echo $archive_left;?>
</ul>
<ul class="sb_double_column_list alignright">
<?php echo $archive_right;?>
</ul>
</div>
Any help or advice would be wholeheartedly appreciated. I'm in a real rut with this!

Okay after messing around a bit I managed to modify wp_get_archives into my own custom function which allowed me to pass it through the column creating code in a similar manner to how I did when trying to use wp_get_archives on its own.
The code is as follows.
function sidebar_archive_list($args = '') {
global $wpdb, $wp_locale;
$defaults = array(
'limit' => '24',
'format' => 'html', 'before' => '',
'after' => '', 'show_post_count' => false,
'echo' => 0, 'order' => 'DESC',
);
$r = wp_parse_args( $args, $defaults );
extract( $r, EXTR_SKIP );
if ( '' != $limit ) {
$limit = absint($limit);
$limit = ' LIMIT '.$limit;
}
$order = strtoupper( $order );
if ( $order !== 'ASC' )
$order = 'DESC';
//filters
$where = apply_filters( 'getarchives_where', "WHERE post_type = 'post' AND post_status = 'publish'", $r );
$join = apply_filters( 'getarchives_join', '', $r );
$output = '';
$query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date $order $limit";
$key = md5($query);
$cache = wp_cache_get( 'wp_get_archives' , 'general');
if ( !isset( $cache[ $key ] ) ) {
$arcresults = $wpdb->get_results($query);
$cache[ $key ] = $arcresults;
wp_cache_set( 'wp_get_archives', $cache, 'general' );
} else {
$arcresults = $cache[ $key ];
}
if ( $arcresults ) {
$afterafter = $after;
foreach ( (array) $arcresults as $arcresult ) {
$url = get_month_link( $arcresult->year, $arcresult->month );
$year = date("y", strtotime($arcresult->year));
$text = sprintf(__('%1$s %2$d'), $wp_locale->get_month_abbrev($wp_locale->get_month($arcresult->month)), $year);
if ( $show_post_count )
$after = ' ('.$arcresult->posts.')' . $afterafter;
$output .= get_archives_link($url, $text, $format, $before, $after);
}
}
if ( $echo ) // If "echo" has been defined as having a value (e.g: 1) then echo $output, if it has no value (0) then return the value.
echo $output;
else
return $output;
}
Just drop it into the functions.php and then use the sidebar_archive_list(). Please note that the function only supports the monthly display and I've altered the defaults slightly from wp_get_archives. So it will return the array rather than echoing it and the default limit is 24 (12 months down one column and vice versa).

Related

How do I get Elementor's custom query filter to work with both GET and pagination?

I am trying to make a custom filter for the loop grid in Elementor, and am having an issue. This code lists the items as they should be listed.
function my_query_by_filter( $query ) {
$meta_query = $query->get( 'meta_query' );
// If there is no meta query when this filter runs, it should be initialized as an empty array.
if ( ! $meta_query ) {
$meta_query = [];
}
$floorplans = '93';
//$floorplans = $_GET['floorplans'];
$meta_query[] = [
'key' => 'layout',
'value' => $floorplans,
'compare' => 'in',
];
$query->set( 'meta_query', $meta_query );
}
add_action( 'elementor/query/filter_condos', 'my_query_by_filter' );
While this code only shows correctly for the first page when using pagination.
function my_query_by_filter( $query ) {
$meta_query = $query->get( 'meta_query' );
// If there is no meta query when this filter runs, it should be initialized as an empty array.
if ( ! $meta_query ) {
$meta_query = [];
}
//$floorplans = '93';
$floorplans = $_GET['floorplans'];
$meta_query[] = [
'key' => 'layout',
'value' => $floorplans,
'compare' => 'in',
];
$query->set( 'meta_query', $meta_query );
}
add_action( 'elementor/query/filter_condos', 'my_query_by_filter' );
Please also note that this is a simplification of the code to hilight the issue. My actual code looks like this.
function my_query_by_filter( $query ) {
$start_time = hrtime(false);
// Get current meta Query
$meta_query = $query->get( 'meta_query' );
// If there is no meta query when this filter runs, it should be initialized as an empty array.
if ( ! $meta_query ) {
$meta_query = [];
}
global $wpdb;
//filter based on city
$city = $_GET['city'];
echo '<script>console.log("city: ' . $city . '");</script>';
$floorplans = array();
$layouts = $wpdb->get_results('SELECT term_id FROM wp_termmeta INNER JOIN wp_postmeta ON wp_termmeta.meta_value=wp_postmeta.post_id WHERE wp_postmeta.meta_key="city" AND wp_postmeta.meta_value="' . $city . '" GROUP BY term_id', ARRAY_N);//returns each layout //I will take care of security flaws later
foreach($layouts as $layout) {
$floorplans[] = '' . $layout[0];
}
$meta_query[] = [
'key' => 'layout',
'value' => $floorplans,
'compare' => 'in',
];
$query->set( 'meta_query', $meta_query );
$end_time = hrtime(false);
$execution_time = ($end_time[1] - $start_time[1]);
echo '<script>console.log("Query took ' . $execution_time . ' nanoseconds to execute");</script>';
}
add_action( 'elementor/query/filter_condos', 'my_query_by_filter' );
I am using Elementor Pro and Pods Admin, along with code snippets.
I have tried setting both global and static variables, along with everything that I can think of, but nothing has worked so far. If there is a better technique to do this, I am open to hearing about it. I just need the user to be able to filter.

CakePHP 3.6 - Find All Records Created Between Two Dates

I am looking for all records between two dates
My variables
$start = '01/01/2009';
$end = '07/24/2019';
I have tried
$gross = $this->CartOrders->find('all')->where(['placed >=' => $start])->andWhere(['placed <=' => $end])->all();
Query Snippet for above
... FROM cart_orders CartOrders
WHERE (placed >= :c0 AND placed <= :c1)
[params] => Array (
[:c0] => Array ( [value] => 01/01/2009 [type] => datetime [placeholder] => c0 )
[:c1] => Array ( [value] => 07/24/2019 [type] => datetime [placeholder] => c1 ) )
Results in
Cake\ORM\ResultSet Object ( [items] => Array ( ) )
I have also tried
$gross = $this->CartOrders->find('all')->where(function($exp) use($start,$end) {
$exp->lte('placed', $end);
$exp->gte('placed', $start);
return $exp;
})->all();
I also have tried
$gross = $this->CartOrders->find('all')->where(function($q) use($start,$end) {
return $q->between('CartOrders.placed', $start, $end, 'date');
})->all();
Any ideas on how I can accomplish this?
Use QueryExpression
use Cake\Database\Expression\QueryExpression;
$query = $this->CartOrders->find()
->where(function (QueryExpression $exp, Query $q) use ($start,$end){
return $exp->between('placed', $start, $end);
});
Probably add a time at condition if the user tried to search within the same day
return $exp->between('placed', $start . " 00:00:00", $end . " 23:59:59");
Try using
$this->CartOrders->find('all', array('conditions' => array(
'date(placed) BETWEEN "'.$start.'" AND "'.$end.'"')));
It's an unorthodox solution but its something that has worked for multiple scenarios for me
$this->set('gross',$this->CartOrders-> find(
'all', array(
'conditions' => array(
'CartOrders.placed >=' => $start,
'CartOrders.placed <=' => $end
))
)); // here gross is a variable to store the data from DB and CartOders is the Model name
This turned out to be a date format issue.
The following solved my problem.
$start = '01/01/2009';
$end = '07/24/2019';
$start = DateTime::createFromFormat('d/m/Y', $start);
$end = DateTime::createFromFormat('d/m/Y', $end);
$gross = $this->CartOrders->find('all')->where([
'placed >=' => $start->format('Y-m-d')
])->andWhere([
'placed <=' => $end->format('Y-m-d')
])->all();
This link helped
PHP convert date format dd/mm/yyyy => yyyy-mm-dd

Save array from drop-down menu in Wordpress

I have successfully pulled a custom post type through into a drop-down that is in a custom meta box. However, when displaying it on the front end I would like to also provide a link to the actual post, not just the name of the post. So I am guessing I need to save this as an array? Is this possible through a drop-down? Confused on how I should approach this. Any help is greatly appreciated.
Here is what I have so far:
// Add Meta Box To Select Overseeing Pastor
add_action('admin_init', 'ministry_select_add_meta');
function ministry_select_add_meta(){
add_meta_box('ministry_select_post', __('Overseeing Pastor'), 'ministry_select_meta', 'ministry', 'side');
}
function ministry_select_meta( $post ) {
$values = get_post_custom( $post->ID );
$selected = isset( $values['pastor_select'] ) ? esc_attr( $values['pastor_select'][0] ) : '';
wp_nonce_field( 'my_meta_box_nonce', 'meta_box_nonce' );
?>
<select name="pastor_select">
<?php
$args = array(
'post_type' => 'employee',
'position' => 'pastor'
);
$pastorList = new WP_Query($args); while ($pastorList->have_posts()) : $pastorList->the_post();
$is_selected = (get_the_title() == $selected) ? 'selected="selected"' : '';
echo '<option value="'.get_the_title().'" '.$is_selected.'>'.get_the_title().'</option>';
endwhile; wp_reset_postdata();
?>
</select>
<?php
}
add_action( 'save_post', 'ministry_select_save' );
function ministry_select_save( $post_id )
{
// Stop If Autosaving
if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
// Stop If Nonce Can't Be Verified
if( !isset( $_POST['meta_box_nonce'] ) || !wp_verify_nonce( $_POST['meta_box_nonce'], 'my_meta_box_nonce' ) ) return;
// Stop If Unauthorized User
if( !current_user_can( 'edit_post' ) ) return;
// Make Sure Data Is Set Then Save
if( isset( $_POST['pastor_select'] ) )
update_post_meta( $post_id, 'pastor_select', esc_attr( $_POST['pastor_select'] ) );
}
To get the link of a Post you can use the get_permalink function
<?php $permalink = get_permalink( ); ?>
or like this if you are outside the Loop
<?php $permalink = get_permalink( $post->ID ); ?>
You can use that to print it in any place on your HTML code.
If what you want is go to the Post URL when the Post Title is selected in the Drop Down you can use JavaScript code to do that, doing something like:
<select name="pastor_select" onchange='location=this.options[this.selectedIndex].value;'>
<?php
$args = array(
'post_type' => 'employee',
'position' => 'pastor'
);
$pastorList = new WP_Query($args); while ($pastorList->have_posts()) : $pastorList->the_post();
$is_selected = (get_the_title() == $selected) ? 'selected="selected"' : '';
echo '<option value="'.get_permalink( ).'" '.$is_selected.'>'.get_the_title().'</option>';
endwhile; wp_reset_postdata();
?>
</select>
If what you want is save some POST information, is recommended save the ID of the POST, so later you can retrieve any data for that POST, what if you want to store permalink and title you can combine the functions get_permalink( ); and get_the_title(); in the select "value" attribute.
So I came up with a different solution. Instead of trying to save an array, I just saved the post ID which would allow me access to the title of the post as well as the permalink.
This is my modified code
<select name="pastor_select">
<?php
$args = array(
'post_type' => 'employee',
'position' => 'pastor'
);
$pastorList = new WP_Query($args); while ($pastorList->have_posts()) : $pastorList->the_post();
$employeeID = get_the_ID(); // THIS FIXED THE PROBLEM
$is_selected = ($employeeID == $selected) ? 'selected="selected"' : '';
echo '<option value="'.$employeeID.'" '.$is_selected.'>'.get_the_title().'</option>';
endwhile; wp_reset_postdata();
?>
</select>
And this is how I am calling it on the front end
<?php
$id = $post_meta_data['pastor_select'][0];
echo '<a href="'.get_permalink($id).'">';
echo get_the_title($id);
echo '</a>';
?>

How to save values of an array to a table?

I have these codes on my add view
$status = array('0' => 'Resolved', '1' => 'Assigned/Unresolved', '2' => 'Suspended', '3' => 'Closed', '4' => 'Bypassed');
echo $this->Form->input('status', array('options' => $status));
and instead of saving the value (e.g. Resolved) to the table, it saves the index of the array. Any ideas?
You should do something like this:
$status = array('resolved' => 'Resolved', 'assigned' => 'Assigned/Unresolved'...);
It saves the index of the array. But this is not a good practice, instead try using enums. Check this out:
/**
* Get Enum Values
* Snippet v0.1.3
* http://cakeforge.org/snippet/detail.php?type=snippet&id=112
*
* Gets the enum values for MySQL 4 and 5 to use in selectTag()
*/
function getEnumValues($columnName=null, $respectDefault=false)
{
if ($columnName==null) { return array(); } //no field specified
//Get the name of the table
$db =& ConnectionManager::getDataSource($this->useDbConfig);
$tableName = $db->fullTableName($this, false);
//Get the values for the specified column (database and version specific, needs testing)
$result = $this->query("SHOW COLUMNS FROM {$tableName} LIKE '{$columnName}'");
//figure out where in the result our Types are (this varies between mysql versions)
$types = null;
if ( isset( $result[0]['COLUMNS']['Type'] ) ) { $types = $result[0]['COLUMNS']['Type']; $default = $result[0]['COLUMNS']['Default']; } //MySQL 5
elseif ( isset( $result[0][0]['Type'] ) ) { $types = $result[0][0]['Type']; $default = $result[0][0]['Default']; } //MySQL 4
else { return array(); } //types return not accounted for
//Get the values
$values = explode("','", preg_replace("/(enum)\('(.+?)'\)/","\\2", $types) );
if($respectDefault){
$assoc_values = array("$default"=>Inflector::humanize($default));
foreach ( $values as $value ) {
if($value==$default){ continue; }
$assoc_values[$value] = Inflector::humanize($value);
}
}
else{
$assoc_values = array();
foreach ( $values as $value ) {
$assoc_values[$value] = Inflector::humanize($value);
}
}
return $assoc_values;
} //end getEnumValues
Paste that method in your AppModel.
Create the column in your table as an enum with the posible values, and use that method to get them.
Can you not define the index as the value you want?
$status = array('Resolved' => 'Resolved', 'Assigned/Unresolved' => 'Assigned/Unresolved', etc etc );
echo $this->Form->input('status', array('options' => $status));

CakePHP - how to apply translate behavior to existing database?

I have and existing application in CakePHP with a database.
The task is to apply translate behavior to its models. The problem is that i18n.php script just creates _i18n table but doesn't copy existing data to this table.
Don't you know any script that could do that?
Thanks for any help.
I extended the answers from Aziz and MarcoB and created a even more generic CakeShell out of it.
In the method _execute() simply set something like:
$this->_regenerateI18n('BlogPosts', array('title'), 'deu');
And all entries for the Model BlogPosts for the column title in the language deu will be create in the i18n table.
This is CakePHP 2.4 compatible!
<?php
class SetuptranslationsShell extends AppShell {
public function main() {
$selection = $this->in('Start to create translated entries?', array('y', 'n', 'q'), 'y');
if (strtolower($selection) === 'y') {
$this->out('Creating entries in i18n table...');
$this->_execute();
}
}
function _execute() {
$this->_regenerateI18n('BlogPosts', array('title'), 'deu');
$this->_regenerateI18n('BlogTags', array('name'), 'deu');
}
/**
* See http://stackoverflow.com/q/2024407/22470
*
*/
function _regenerateI18n($Model, $fields = array(), $targetLocale) {
$this->out('Create entries for "'.$Model.'":');
if (!isset($this->$Model)) {
$this->{$Model} = ClassRegistry::init($Model);
}
$this->{$Model}->Behaviors->disable('Translate');
$out = $this->{$Model}->find('all', array(
'recursive' => -1,
'order' => $this->{$Model}->primaryKey,
'fields' => array_merge(array($this->{$Model}->primaryKey), $fields))
);
$this->I18nModel = ClassRegistry::init('I18nModel');
$t = 0;
foreach ($out as $v) {
foreach ($fields as $field) {
$data = array(
'locale' => $targetLocale,
'model' => $this->{$Model}->name,
'foreign_key' => $v[$Model][$this->{$Model}->primaryKey],
'field' => $field,
'content' => $v[$Model][$field],
);
$check_data = $data;
unset($check_data['content']);
if (!$this->I18nModel->find('first', array('conditions' => $check_data))) {
if ($this->I18nModel->create($data) AND $this->I18nModel->save($data)) {
echo '.';
$t++;
}
}
}
}
$this->out($t." entries written");
}
}
As far as I know, there's no way to do this. Moreover, because of the way the i18n table is configured to work, I think there's a better solution. A while back, I wrote a patch for the TranslateBehavior that will keep you from having to copy existing data into the i18n table (that felt insanely redundant to me and was a huge barrier to implementing i18n). If no record for that model exists in the i18n table, it will simply read the model record itself as a fallback.
Unfortunately, the Cake team appears to have moved everything to new systems, so I can no longer find either the ticket or the patch that I submitted. My patched copy of the TranslateBehavior is in my Codaset repository at http://codaset.com/robwilkerson/scratchpad/source/master/blob/cakephp/behaviors/translatable.php.
As you might expect, all of the usual warnings apply. The patched file was developed for 1.2.x and works for my needs, by YMMV.
try to use it
function regenerate()
{
$this->Article->Behaviors->disable('Translate');
$out = $this->Article->find('all', array('recursive'=>-1, 'order'=>'id'));
$t = $b = 0;
foreach($out as $v){
$title['locale'] = 'aze';
$title['model'] = 'Article';
$title['foreign_key'] = $v['Article']['id'];
$title['field'] = 'title';
$title['content'] = $v['Article']['title'];
if($this->Article->I18n->create($title) && $this->Article->I18n->save($title)){
$t++;
}
$body['locale'] = 'aze';
$body['model'] = 'Article';
$body['foreign_key'] = $v['Article']['id'];
$body['field'] = 'body';
$body['content'] = $v['Article']['body'];
if($this->Article->I18n->create($body) && $this->Article->I18n->save($body)){
$b++;
}
}
}
Thanks Aziz. I modified your code to use it within the cakeshell
(CakePHP 2.3.8)
function execute() {
$this->out('CORE_PATH: '. CORE_PATH. "\n");
$this->out('CAKEPHP_SHELL: '. CAKEPHP_SHELL. "\n");
$this->out('Migrate BlogPosts');
$this->regenerateI18n('BlogPost', 'title', 'BlogPostI18n');
}
/**
* #param string $Model
* #param string $Field
* #param string $ModelI18n
*/
function regenerateI18n($Model = null, $Field = null, $ModelI18n = null)
{
if(!isset($this->$Model))
$this->$Model = ClassRegistry::init($Model);
if(!isset($this->$ModelI18n))
$this->$ModelI18n = ClassRegistry::init($ModelI18n);
$this->$Model->Behaviors->disable('Translate');
$out = $this->$Model->find('all', array('recursive'=>-1, 'order'=>'id'));
$t = 0;
foreach($out as $v){
$data = array(
'locale' => 'deu',
'model' => $this->$Model->name,
'foreign_key' => $v[$Model]['id'],
'field' => $Field,
'content' => $v[$Model][$Field],
);
if($this->$ModelI18n->create($data) && $this->$ModelI18n->save($data)){
echo '.';
$t++;
}
}
$this->out($t." Entries written");
}

Resources