I am progressively solving (always with your help) this Eloquent query but have not managed to completely solve it. Sorry to still staying at it, but I am struggling to my best.
I have written the query in two flavors. I would like to keep the Laravel one since I am using that but it is that one the one which doesn't work:
WORKS WHEN I DO IT ON PHPMYADMIN SQL WINDOW:
select properties.id, title, city, price, postedat, propertytype,
( 3959 * acos( cos( radians(43) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-1.5) ) + sin( radians(43) ) * sin( radians( lat ) ) ) ) AS distance
from properties
join addresses
on properties.id_address_fk = addresses.id
where city = 'Biarritz'
HAVING distance < 100
ORDER BY distance
LIMIT 0, 20;
BELOW DOESN'T WORK (I get a: trying to get property from a non-object error)
$properties = DB::table('properties')
->join('addresses', 'properties.id_address_fk', '=', 'addresses.id')
->select('properties.id', 'title', 'city', 'price', 'postedat', 'propertytype',
DB::raw('3959 * acos( cos( radians(43) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians (-1.5) )+ sin( radians(43) ) * sin( radians( lat ) ) ) as distance') );
if (!empty($location)) {
$properties = $properties->where('location', '=', $location);
}
if (!empty($propertytype)) {
$properties = $properties->where('propertytype', '=', $propertytype);
}
if (!empty($bedrooms)) {
$properties = $properties->where('bedrooms', '>=', $bedrooms);
}
if (!empty($transaction)) {
$properties = $properties->where('transaction', '=', $transaction);
}
if (!empty($minprice)) {
$properties = $properties->where('price', '>=', $minprice);
}
if (!empty($maxprice)) {
$properties = $properties->where('price', '<=', $maxprice);
}
$properties->having('distance', '<', 100)
->orderBy('distance', 'desc')
->skip(0)
->take(5)
->get();
return View::make('propertyfound', compact('premium', 'properties'));
UPDATE:
Of course, the errors are when it tries to get property from a non-object
#foreach($properties as $propfound)
<div class="row premium">
<div class="col-sm-3 col-xs-3">
<a href="{{URL::route('property', array($propfound->id) )}}" class="thumbnail " >{{ HTML::image("public/css/images/houses/$propfound->houses1")}}</a>
<h5>Guide price: £ {{$propfound->price}}</h5>
<h6>Bedrooms: {{$propfound->bedrooms}}</h6>
<h6>Days on site: 4</h6>
<h6>Sqft: {{$propfound->m2}}</h6>
</div>
<div class="col-sm-9">
<h3>{{$propfound->title}}</h3>
<p>{{$propfound->description}}</p>
<p class="hidden-xs">More details, 14 photos, floorplan and brochure | Save property | Contact agent | Upgrade listing</p>
<p class="hidden-xs">Marketed by Knight Frank, Richmond. Telephone: 0843 314 8224 BT 4p/min </p>
</div>
</div>
<hr />
#endforeach
You can't simply chain ->get() to the original DB object and get results.
// Doesn't work
$object->having()->take()->get();
foreach ( $object as $value ) {
// $value is some part of the query builder/eloquent object
}
You have to assign to a new variable
// Works
$results = $object->having()->take()->get();
foreach ( $results as $value ) {
// $value is db result
}
Related
I'm trying to paginate items in a livewire component, I have this query
protected $listeners = [
'franchises:coords' => 'getFranchises'
];
public $franchiseList;
public function render()
{
return view('livewire.franchise-list', ['franchiseList' => $this->franchiseList]);
}
public function getFranchises($coords)
{
if ($coords) {
$latitude = json_decode($coords)[0];
$longitude = json_decode($coords)[1];
$radiusInMeters = 800;
$this->franchiseList = Franchise::select(DB::raw('*, ( 6371000 * acos( cos( radians(' . $latitude . ') )
* cos( radians( latitude ) ) * cos( radians( longitude ) - radians(' . $longitude . ') )
+ sin( radians(' . $latitude . ') ) * sin( radians( latitude ) ) ) ) AS distance'))
->having('distance', '<', $radiusInMeters)
->orderBy('distance')
->paginate(8);
}
}
the component is included in my "maps.blade"
<div class="lg:basis-1/4 md:basis-1/4 sm:px-6 lg:px-8">
<livewire:franchise-list/>
</div>
and in my blade view, I have this
#if(!empty($franchiseList))
{{$franchiseList->links()}}
#endif
but I get this error
Livewire component's [franchise-list] public property [franchiseList]
must be of type: [numeric, string, array, null, or boolean]. Only
protected or private properties can be set as other types because
JavaScript doesn't need to access them.
If I try to change pagination by adding these lines to the getFranchises function and adding $links to public
public $franchiseList, $links;
//after paginate
$this->links = $this->franchiseList;
$this->franchiseList = collect($this->franchiseList->items);
and in the blade change to this
#if(!empty($franchiseList) && $links->links())
{{$links->links()}}
#endif
I get this error
Error Cannot access protected property
Illuminate\Pagination\LengthAwarePaginator::$items
How can I paginate in livewire? where is the problem?
Ok, as I guess your issue is right there in render method. Instead of doing what you're doing, change this once you have in the mount the property initialized
public $franchiseList;
public function mount()
{
$this->franchiseList = [];
}
public function render()
{
return view('livewire.franchise-list');
}
So, as the property is public you can bind it in blade directly. At load component, it is an empty array and on event (in listener) this will change to passed value. That's the idea, but if you do this ['franchiseList' => $this->franchiseList] it's going to be crashed (when same name). I can't tell you why because I really don't know the answer but I know this happens from my experience.
I have a fully working AJAX load more button on my Wordpress posts. On click, it loads the next page of posts and appends them directly to the end of the first set.
I now need to create category filters. So when a user clicks a category name, it updates the results below via AJAX to only show posts in that category - BUT I need the load more ajax to continue to work correctly. I've tried hashing together two different AJAX calls, and also tried combining both AJAX into one, but I can't seem to get them talking...
Here's the code:
functions.php
function we_title_filters() {
if(is_home()): ?>
<div class="title-filters">
<h3 class="widget-title" style="display:inline-block;">Latest News</h3>
<div class="filters">
<?php
$include = array(3651, 7, 2828, 2829, 2172);
$categories = get_categories( array(
'orderby' => 'name',
'parent' => 0,
'include' => $include
) );
foreach ( $categories as $category ) {
printf( '%2$s',
esc_url( get_category_link( $category->term_id ) ),
esc_html( $category->name )
);
}
?>
<div class="dropdown" style="float:right;">
<span class="dropbtn">More</span>
<ul class="more-list">
<?php
$include = array(3651, 7, 2828, 2829, 2172);
$categories = get_categories( array(
'orderby' => 'name',
'parent' => 0,
'include' => $include
) );
foreach ( $categories as $category ) {
printf( '<li>%2$s</li>',
esc_url( get_category_link( $category->term_id ) ),
esc_html( $category->name )
);
}
?>
</ul>
</div>
</div>
</div>
<?php endif;
}
This outputs the filters in a list. On click of one of them, it needs to AJAX change the results, but also keep the load more button working, within that category
/* AJAX load more stuff */
/**
*
* Infinite Scroll
*
* #since 1.0.0
*
* #author Lauren Gray
* #author Bill Erickson
* #link http://www.billerickson.net/infinite-scroll-in-wordpress
*
* Enqueue necessary JS file and localize.
*
*/
add_action( 'wp_enqueue_scripts', 'sn_infinite_scroll_enqueue' );
function sn_infinite_scroll_enqueue() {
if ( ! is_singular() ) {
global $wp_query;
$args = array(
'nonce' => wp_create_nonce( 'be-load-more-nonce' ),
'url' => admin_url( 'admin-ajax.php' ),
'query' => $wp_query->query,
'maxpage' => $wp_query->max_num_pages,
);
wp_enqueue_script( 'sn-load-more', get_stylesheet_directory_uri() . '/js/load-more.js', array( 'jquery' ), '1.0', true );
wp_localize_script( 'sn-load-more', 'beloadmore', $args );
}
}
/**
*
* Infinite Scroll
*
* #since 1.0.0
*
* #author Lauren Gray
* #author Bill Erickson
* #link http://www.billerickson.net/infinite-scroll-in-wordpress
*
* Parse information
*
*/
add_action( 'wp_ajax_sn_infinite_scroll_ajax', 'sn_infinite_scroll_ajax' );
add_action( 'wp_ajax_nopriv_sn_infinite_scroll_ajax', 'sn_infinite_scroll_ajax' );
function sn_infinite_scroll_ajax() {
if ( ! is_singular() ) {
check_ajax_referer( 'be-load-more-nonce', 'nonce' );
$excluded_ids = get_field('top_stories', option);
$args = isset( $_POST['query'] ) ? array_map( 'esc_attr', $_POST['query'] ) : array();
$args['post_type'] = isset( $args['post_type'] ) ? esc_attr( $args['post_type'] ) : 'post';
$args['paged'] = esc_attr( $_POST['page'] );
$args['post_status'] = 'publish';
$args['post__not_in'] = $excluded_ids;
$pageType = esc_attr( $_POST['pageType'] );
if ( $pageType == "is-home" ) {
$initial = 30;
$ppp = 30;
$columns = 3;
}
else {
$initial = 30;
$ppp = 30;
$columns = 3;
}
$args['posts_per_page'] = $ppp;
$args['offset'] = $initial + ( $ppp * ( $_POST['page'] ) );
ob_start();
$loop = new WP_Query( $args );
if( $loop->have_posts() ): while( $loop->have_posts() ): $loop->the_post();
sn_post_summary( $loop->current_post, $columns );
endwhile;
endif;
wp_reset_postdata();
$page = esc_attr( $_POST['page'] );
}
$data = ob_get_clean();
wp_send_json_success( $data );
wp_die();
}
}
/**
*
* Infinite Scroll
*
* #since 1.0.0
*
* #author Lauren Gray
* #author Bill Erickson
* #link http://www.billerickson.net/infinite-scroll-in-wordpress
*
* Output articles
*
*/
function sn_post_summary( $count, $columns ) {
// Be able to convert the number of columns to the class name in Genesis
$fractions = array( '', 'half', 'third', 'fourth', 'fifth', 'sixth' );
// Make a note of which column we're in
$column_number = ( $count % $columns ) + 1;
// Add one-* class to make it correct width
$countClasses = sprintf( 'one-' . $fractions[$columns - 1] . ' ', $columns );
// Add a class to the first column, so we're sure of starting a new row with no padding-left
if ( 1 == $column_number )
$countClasses .= 'first ';
remove_action( 'genesis_entry_content', 'genesis_do_post_content' );
remove_action( 'genesis_entry_header', 'genesis_post_info', 12 );
remove_action( 'genesis_entry_content', 'genesis_do_post_image', 8 );
add_action( 'genesis_entry_header', 'genesis_do_post_image', 8 );
echo '<article class="' . $countClasses . implode( ' ', get_post_class() ) . '">'; // add column class
do_action( 'genesis_entry_header' );
echo '</article>';
}
This is a modified version of the infinite scroll loader found here: https://gist.github.com/graylaurenm/86daa4f23aa8749c0933f72133ac7106
I removed the infinite scroll options so it only loads on click of the button.
I have a filtration query looks like this.
public function index( Request $request ) {
$dateFrom = $request->get( 'dateFrom' );
$dateTo = $request->get( 'dateTo' );
$status = $request->get( 'orderState' );
$orders = ( new OrderList() )
->whereHas( 'orderDetail', function ( $query ) {
$query->where( 'supplier_id', Auth::guard( 'supplier' )->user()->id );
} )
->with( 'deliveryList' )
->with( 'orderDetail' )
->when( $dateFrom, function ( $query ) use ( $dateFrom, $dateTo ) {
$query->whereBetween( 'created_at', [ $dateFrom, $dateTo ] );
} )
->when( $status, function ( $query ) use ( $status ) {
$query->where( 'order_state_id', $status )->get();
} )->get();
$productList = $orders->orderDetail;
dd( $productList );
//Order Status
$orderStates = OrderState::listsTranslations( 'states' )->pluck( 'states', 'id' )->toArray();
return view( 'supplierComponents.order_list', compact( 'orders', 'orderStates' ) );
}
now this query will output a list of orders with the orders details.
what I need is group the products in each order and get the quantity
the last view should be something like this
product name quantity
-------------------------------
Tea 5
-------------------------------
juice 3
-------------------------------
My Models
OrderLst
public function orderDetail() {
return $this->hasMany( OrderDetail::class, 'order_id' );
}
Update
thought I will never get it the right way from controller and I didn't think about try to get it in the blade directly
so What I did is shifted to the blade trying to make it there.
Here what I did
#foreach($orders as $order)
#foreach($order->orderDetail as $items)
<tr>
<td>{!! $items->quantity !!}</td>
<td>{!! $items->product_name !!}</td>
</tr>
#endforeach
#endforeach
Now I am getting all the products listed as I need but try to Group them
like that
#foreach($orders as $order)
#foreach($order->orderDetail->groupBy('product_id') as $items)
<tr>
<td>{!! $items->quantity !!}</td>
<td>{!! $items->product_name !!}</td>
</tr>
#endforeach
#endforeach
Got: Property [quantity] does not exist on this collection instance.
$orders = ( new OrderList() )
->whereHas( 'orderDetail', function ( $query ) {
$query->where( 'supplier_id', Auth::guard( 'supplier' )->user()->id );
} )
->with( 'deliveryList' )
->with(['orderDetail' => function ($query) {
$query->select(DB::raw('*, sum(quantity) as quantity_sum'))->groupBy('product_id')->get();
}])
->when( $dateFrom, function ( $query ) use ( $dateFrom, $dateTo ) {
$query->whereBetween( 'created_at', [ $dateFrom, $dateTo ] );
} )
->when( $status, function ( $query ) use ( $status ) {
$query->where( 'order_state_id', $status )->get();
} )->get();
Untested.
I have some problems with sorting datas when i try type distance for example to 5 i have this error:
Unknown column 'dystans' in 'having clause'
SELECT COUNT(*) AS `numrows` FROM `meet` JOIN `category` ON `category`.`id` = `meet`.`category_id` HAVING `dystans` <= '5' ORDER BY `when` ASC
Filename: C:/xamppNew/htdocs/dzielimypasje/application/controllers/Meetings.php
Line Number: 177
Im using codeigniter. Line 177 is this
$data['count'] = $count = $this->db->count_all_results();
My controller:
$lat = $this->session->userdata('lat');
$lng = $this->session->userdata('lng');
// To pagination
$this->load->library('pagination');
$limit = 10;
$offset = $this->uri->segment(4);
$this->db->start_cache();
$this->db->select('*, meet.id,
(6731 * acos( cos( radians( '.$lat.')) * cos( radians( meet.lat)) *
cos( radians( meet.lng) - radians( '.$lng.')) + sin( radians( '.$lat.')) *
sin( radians( meet.lat)))) AS dystans');
//data from search engine
$level = $this->input->post('level');
$cat = $this->input->post('category');
$dystans = $this->input->post('dystans');
$when = $this->input->post('when');
if ($level) {
$this->db->where('level', $level);
}
if ($cat) {
$this->db->where('category_id', $cat);
}
if ($when) {
$this->db->where('when <=', $when);
}
if ($dystans) {
$this->db->having('dystans <=', $dystans);
}
$this->db->order_by('when', 'ASC');
$this->db->from('meet');
$this->db->join('category', 'category.id = meet.category_id');
$this->db->stop_cache();
// count for pagination
$data['count'] = $count = $this->db->count_all_results();
// to pagination
$this->db->limit($limit, $offset);
$data['meetings'] = $this->db->get();
$this->db->flush_cache();
$config = some config for pagination ...
$this->pagination->initialize($config);
$data['pagination'] = $this->pagination->create_links();
$this->load->view( 'site/meetings/index' , $data );
view:
<select type="text" name="dystans" placeholder="distance">
<option value="">Odległość</option>
<option value="1">Do 1 km</option>
<option value="2">Do 2km</option>
<option value="5">Do 5km</option>
<option value="10">Do 10km</option>
</select>
Any idea how to solve this ?? Propably its something with cache but im not sure, im new in this.
Alright. I dug into this and fixed everything I could find. I ditched active record for straight database access with query() function because...well...I have never liked active record and personally believe it is much much cleaner to write your query in an admin tool and copy it into your model. This gives you access to ALL of mysql's capabilities without having to jump through hoops (not to mention if you wind up using a different code base in the future you already have your queries and don't have to dig back through all of the active record code).
The main reason you were breaking at the dystans block was because you were trying to use a dynamically generated column within your where clause. The where clause only knows about columns that exist within the tables you have joined on. So to do what you are needing you have to run back through your formula.
I have also broken this out so that all of the filters will work in conjunction with one another. A user can now use filter when with filter dystans and the query will be build accordingly.
I haven't tested this code because obviously I do not have your database but it is the same way I code all of my applications. If you have trouble please let me know.
Also you should really consider breaking out your business logic or at least database interaction into a codeigniter model. Controllers should only ever act as a router between your models and views.
The link to my post on codeigniter pagination
<?php
$lat = $this->session->userdata('lat');
$lng = $this->session->userdata('lng');
// To pagination
$this->load->library('pagination');
$limit = 10;
$offset = (isset($this->uri->segment(4)) && is_numeric($this->uri->segment(4))) ? $this->uri->segment(4) : 0;
$bindArray = array();
$query = "SELECT *,"
. " (6731 * acos(cos( radians(?)) * cos( radians( meet.lat))"
. " * cos( radians( meet.lng) - radians(?)) + sin( radians(?))"
. " * sin( radians( meet.lat)))) AS dystans";
$bindArray[] = $lat;
$bindArray[] = $lng;
$bindArray[] = $lat;
$query .= " FROM meet JOIN category ON category.id = meet.category_id"
. " WHERE TRUE = TRUE";
//data from search engine
$level = $this->input->post('level');
$cat = $this->input->post('category');
$dystans = $this->input->post('dystans');
$when = $this->input->post('when');
if($level){
$query .= " AND meet.level = ?";
$bindArray[] = $level;
}
if($cat){
$query .= " AND meet.category_id = ?";
$bindArray[] = $cat;
}
if($when){
$query .= " AND meet.when <= ?";
$bindArray[] = $when;
}
if($dystans){
$query .= " AND (6731 * acos(cos( radians(?)) * cos( radians( meet.lat))"
. " * cos( radians( meet.lng) - radians(?)) + sin( radians(?))"
. " * sin( radians( meet.lat)))) <= ?";
$bindArray[] = $dystans;
}
$query .= "LIMIT ?,?";
$bindArray[] = $offset;
$bindArray[] = $limit;
$query_result = $this->db->query($query, $bindArray);
$data['count'] = $query_result->num_rows();
$data['meetings'] = $query_result->result();
$config = 'YOUR PAGINATION CONFIGURATION';
$this->pagination->initialize($config);
$data['pagination'] = $this->pagination->create_links();
$this->load->view( 'site/meetings/index' , $data );
I have been using K2 Scroller module developed by Land of Coder. When I use this module to display same category items in the items view the module displays items in the ascending order of the date created and it is the default and only setting available in the module parameter settings in the back-end. However, I want the module to display the items in date wise descending order. So I chose to edit the default code in the helper which I suppose is used to process items based on the back-end settings. And this is the part of the code in the helper file which I think controls the order:
public function __getList( $params ){
global $mainframe;
/*some irrelevant code removed*/
$condition = $this->buildConditionQuery( $params );
$limitDescriptionBy = $params->get('limit_description_by','char');
$ordering = $params->get( 'k2_ordering', 'created_desc');
$limit = $params->get( 'limit_items', 5 );
$ordering = str_replace( '_', ' ', $ordering );
$my = &JFactory::getUser();
$aid = $my->get( 'aid', 0 );
/*some irrelevant code removed*/
$extraURL = $params->get('open_target')!='modalbox'?'':'&tmpl=component';
$excludeIds = $params->get('exclude_ids', '');
$db = &JFactory::getDBO();
$date =& JFactory::getDate();
$now = $date->toMySQL();
$dateFormat = $params->get('date_format', 'DATE_FORMAT_LC3');
$limitDescriptionBy = $params->get('limit_description_by','char');
$isAuthor= $params->get('itemAuthor',0);
require_once ( JPath::clean(JPATH_SITE.'/components/com_k2/helpers/route.php') );
$query = "SELECT a.*, cr.rating_sum/cr.rating_count as rating, c.name as categoryname,
c.id as categoryid, c.alias as categoryalias, c.params as categoryparams, cc.commentcount as commentcount".
" FROM #__k2_items as a".
" LEFT JOIN #__k2_categories c ON c.id = a.catid" .
" LEFT JOIN #__k2_rating as cr ON a.id = cr.itemid".
" LEFT JOIN (select cm.itemid as id, count(cm.id) as commentcount from #__k2_comments as cm
where cm.published=1 group by cm.itemid) as cc on a.id = cc.id";
$query .= " WHERE a.published = 1"
. " AND a.access get('featured_items_show','0') == 0 ){
$query.= " AND a.featured != 1";
} elseif( $params->get('featured_items_show','0') == 2 ) {
$query.= " AND a.featured = 1";
}
if( trim($excludeIds) ){
$query .= " AND a.id NOT IN(".$excludeIds.") ";
}
$query .= $condition . ' ORDER BY ' . $ordering;
$query .= $limit ? ' LIMIT ' . $limit : '';
$db->setQuery($query);
$data = $db->loadObjectlist();
/*some irrelevant code removed*/
return $data;
}
/**
* build condition query base parameter
*
* #param JParameter $params;
* #return string.
*/
public function buildConditionQuery( $params ){
$source = trim($params->get( 'k2_source', 'k2_category' ) );
if( $source == 'k2_category' ){
$catids = $params->get( 'k2_category','');
if( !$catids ){
return '';
}
$catids = !is_array($catids) ? $catids : '"'.implode('","',$catids).'"';
$condition = ' AND a.catid IN( '.$catids.' )';
} else {
$ids = preg_split('/,/',$params->get( 'k2_items_ids',''));
$tmp = array();
foreach( $ids as $id ){
$tmp[] = (int) trim($id);
}
$condition = " AND a.id IN('". implode( "','", $tmp ) ."')";
}
return $condition;
}
Am I editing the right part of the code or am I missing something else.
I am looking forward to your help
Thanks.
Maybe the best way would be to modify the xml file of the module.
I looked at the code of Lof K2Scroller (v 2.2 for Joomla 2.5) and the different ordering options are there.
However if you want to modify the default option, you are in the right file, just replace 'created_desc' for 'created_asc' .