I’m new to CakePHP and I’ve tried searching but I can’t find an answer to this question.
To put it simply, I want the query to be something like this:
SELECT id from posts WHERE id IN (15,18,20);
But I don’t know what to put in the find() call.
This is covered in the CakePHP online manual at http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#complex-find-conditions. Simply specify an array in your conditions:
<?php
$ids = array(1,2,3,4,5,6);
$results = $this->Post->find('all', array(
'conditions' => array(
'Post.id' => $ids
)
));
From the model it would be something like:
$ids = array(15, 18, 20);
$posts = $this->find('all', array(
'conditions' => array(
'Post.id' => $ids
)
);
in the conditions array, you can pass an array of values to be used in the 'IN' clause
From within the posts controller
$id_array = array(15, 18, 20);
$this->Post->find('all', array('Post.id' => $id_array));
More on the subject
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html
$conditions = array("Post.title" => array("First post", "Second post", "Third post"))
$this->find(all,array($conditions));
Chk it if it works.
Related
I have two custom post types, playlists and games. Playlists have a ACF repeater field, that contain games.
My goal: I am trying to build a simple query, that gets all playlists that DO NOT have this game listed in the repeater field AND alll playlists that are empty (do not have any repeater fields / database entries at all). I want to use the get_posts() function to achieve my goal, if possible.
My problem: I can only get the query to show ONLY the playlists that HAVE the game by using the = operator in the "game_filter" meta query compare field (but I want the opposite). If I choose the != or <> or IS NOT operator, it spits out all available posts. And if I also include my "empty_playlists" meta query, it ALWAYS shows me all playlists, no matter what operators I use, even tho it is a OR and the listed posts do have entrys.
I tried a lot but just cant get it to work. All exmaples I found were about different things, mostly about the % problem with the repeater field names and its solution. I hope someone can help me with this real quick. What am I doing wrong? Please help fellow and better coders! :-)
This is my code
$excluded_playlists_game_id = 274;
$user_id = 1;
$args = array(
'post_type' => PSiCorePostTypes::PLAYLISTS,
'author' => $user_id,
'order_by' => 'title',
'order' => 'ASC',
'suppress_filters' => false,
'meta_query' => array(
'relation' => 'OR',
'game_filter' => array(
'type' => 'NUMERIC',
'key' => 'psi_playlist_games_%_item',
'compare' => '!=',
'value' => $excluded_playlists_game_id,
),
'empty_playlists' => array(
'key' => 'psi_playlist_games_%_item',
'compare' => 'NOT EXISTS',
),
),
);
$user_playlists = get_posts( $args );
and this function for the % problem.
add_filter( 'posts_where', 'get_user_playlists_query_allow_wildcard' );
function get_user_playlists_query_allow_wildcard( $where ) {
global $wpdb;
$where = str_replace(
"meta_key = 'psi_playlist_games_%_item",
"meta_key LIKE 'psi_playlist_games_%_item",
$wpdb->remove_placeholder_escape( $where )
);
return $where;
}
I need to insert multioptions to a dropdown list, options taken from a table from my database.
I created the elements like:
$this->add(array(
'name' => 'company',
'type' => 'Zend\Form\Element\Select',
//'multiOptions'=> $options,
'options' => array(
'label' => 'Company',
),
'attributes' => array(
'style' => "float:right;",
),
));
I want to choose from a dropdown list some values that are in a table in my database. For example I have the entity Contacts and I need to choose for the contact a company that is in a table named companies in the database.
After reading on zend framework's site, I tried using this code:
$params = array(
'driver'=>'Pdo_Mysql',
'host'=>'localhost',
'username'=>'root',
'password'=>'',
'dbname' =>'myDataBase'
);
$db = new \Zend\Db\Adapter\Adapter($params);
$sql= new Sql($db);
$select = $sql->select();
$select ->from('companies')
->columns(array('id','company_name'))
->order(" 'company_name' ASC");
I also read on some other sites that I could use a function:
$options = $sql->fetchPairs('SELECT id, name FROM country ORDER BY name ASC');
but it seems it doesn't exist anymore in Zend Framework 2.
Please guys, give me a hand. If the code isn't good and you have a better idea, please tell me.
Thanks in advance!
This is just a quick and dirty answer, but i guess it can get you started.
Create a ServiceFactory, this should be done in a separate factory class instead of a closure, but i still use a closure - faster to write ;)
Get the config from the ServiceLocator so you have access to the DB-Params
Create your default SQL Stuff to retriefe the value_options
Populate the value_options using the setValueOptions($valueOptions) function of your given form-element
Module.php getServiceConfig()
return array(
'factories' => array(
'my-form-factory' => function($serviceLocator) {
$form = new My\Form();
$config = $serviceLocator->get('config');
$db = new \Zend\Db\Auth\Adapter\Adapter($config['dbParams']); //or whatever you named the array key
$sql = //do your SQL Stuff
// This is a fake array, it should be your $sql result in the given format
$result = array('value' => 'label', 'value2' => 'label2');
$form->get('elementToPopulate')->setValueOptions($result);
return $form;
}
)
);
SomeController.php someAction()
$form = $this->getServiceLocator()->get('my-form-factory');
return new ViewModel(array(
'form' => $form
));
I hope this gets you started
you have to add that field validation on controller for setting value in it.
$select = $db->select()->where("state_code = ?",$arr["state_code"]);
$resultSet = $cityObj->fetchAll($select);
$cityArr = $resultSet->toArray();
$city_ar = array();
foreach($cityArr as $city){
$city_ar[$city['id']] = $city['company'];
}
$form->company->setMultiOptions($city_ar);
$form->company->setValue($val["company"]);
by using this code drop down of country have the value that are in resultset array ($resultSet).
I'm trying to display eight of the most relevant posts, in order of relevance, based on tags shared with the current post being viewed and the date created...
Models:
Tag habtm Post
Post habtm Tag
DB:
posts(id, slug, ...)
tags(id, tag, ...)
posts_tags(post_id, tag_id)
Within controller action:
$post = $this->Post->find('first', array('conditions' => array('slug' => $slug)));
$this->set('post', $post);
$tags = $post['Tag'];
$relOrd = '';
foreach($tags as $tag){
$tagId = $tag['id'];
$relOrd .= " + (CASE WHEN PostsTag.tag_id = ".$tagId." THEN 1 ELSE 0 END)";
}
$relOrd = '(' . substr($relOrd, 3) . ') AS Relevance';
$morePosts = $this->Post->find('all', array(
'joins' => array(
array(
'table' => 'posts_tags',
'alias' => 'PostsTag',
'type' => 'LEFT',
'conditions' => array(
'PostsTag.post_id = Post.id',
)
)
),
'group' => 'Post.id',
'fields' => array($relOrd, 'Post.*'),
'order' => array('Relevance' => 'DESC', 'Post.created' => 'DESC'),
'limit' => 8,
));
$this->log($morePosts);
$this->set('morePosts', $morePosts);
It's almost working, although the relevance value is being treated as if each post has only one tag (being only 0 or 1). So it seems that the relevance value for each post is taking either 0 or 1 depending on the posts LAST tag rather than being accumulative based on ALL tags.
First of all, I'd take all of the logic out of the controller. Consider this:
$post = $this->Post->find('first', array('conditions' => array('slug' => $slug)));
$this->set('post', $post);
$this->set('morePosts', $this->Post->findRelevant($post));
Now your controller is easy to read and does it's job. You essentially start by describing what data you want by naming an imaginary model function, and then you write the model code to fulfill that request.
So here's a stab at the model code:
var $actsAs = array('Containable');
function findRelevant($post, $limit = 8) {
// create an array of ids of the tags from this post
$tags = array();
foreach($post['Tag'] as $num => $tag) {
$tags[$tag['id']] = $tag['id'];
}
// find other posts that have any of those tags
$relevant = $this->find('all', array(
'conditions' => array('Post.id <>' => $post['Post']['id']),
'order' => 'Post.created desc',
'contain' => array('Tag' => array('conditions' => array(
'Tag.id' => $tags
))),
));
// count the number of tags of each post and call it relevance
// (this number is essentially the number of tags in common
// with the original post because we used contain to get only
// the tags from the original post)
foreach($relevant as &$p) {
$p['Post']['relevance'] = count($p['Tag']);
}
// sort by relevance
$relevant = Set::sort($relevant, '{n}.Post.relevance', 'desc');
// limit the number of posts returned (defaults to 8)
return array_splice($relevant, 0, $limit);
}
Obviously it would be great to use the database logic to fetch the records (as you are attempting to do) so that it's as fast as possible and so that you minimize the amount of data that you retrieve, but I can't see how to do that for what you're trying to achieve.
This method should work fine and is not database specific. :)
In my CakePHP site, I want to make a drop-down list of all Venues, and any Restaurants that have is_venue=1.
I've tried this in my events_controller:
$venueOptions = array(
'fields' => array('id', 'name_address'),
'order' => array('name'),
'join' => array(
array(
'table' => 'restaurants',
'alias' => 'Restaurants',
'type' => 'inner',
'fields' => array('id', 'name'),
'foreignKey' => false,
'conditions' => array('restaurants.is_venue = 1')
)
),
);
$venues = $this->Event->Venue->find('list', $venueOptions);
But it appears to still just be getting the venues. I don't really need an association between the two, since their associations will both be with an event, not each other.
Where have I gone wrong? Am I close, but just need to tweak this code, or am I just all-together doing it wrong?
I think you could do something along the lines of:
<?php
....
$v = $this->Venue->find( 'list' );
$r = $this->Restaurant->find( 'list' );
$venues = Set::merge( $v, $r );
natcasesort( $venues );
// print_r( $venues );
$this->set( 'venues', $venues );
...
?>
Which is quite like the code above - I just use the Set class and make sure to Controller::set the variable to the view.
Also added some basic sorting to show you one option even though array sorting has nothing really specific to do with CakePHP.
Also fixed some bad variable names where I had originally used $venues, and $restaurants - changed to be consistently $v and $r.
Join will not work if there's no relation between. Venue and Restaurant. You should call them separately and merge the results
$venues = $this->Event->Venue->find('list', $venueOptions);
$restaurants = $this->Event->Restaurant->find('list', array('conditions' => array('is_venue' => '1')));
$results = array_merge($venues, $restaurants);
// sort results
asort($results);
I have thee following simple model:
Item belongsTo CatalogItem
CatalogItem hasMany Item, and belongsTo Section
Section hasMany CatalogItem
I'm trying to get counts of items, grouped by catalogitem, for a certain section-
the equivalent of:
SELECT catalogitem.id, count(*) FROM section LEFT JOIN catalogitem ON section.id=catalogitem.section_id LEFT JOIN item ON item.catalogitem_id=catalogitem.id WHERE section.id=5 GROUP BY catalogitem.id
So simple in sql, yet I can't get it to work with cake models. Can anyone point as to how to do it with cake models, using the model->find?
I can't get it to group by correctly or join correctly on 3 tables :(
Edit:
highly prefer to get the info in single query
Here's a longer way, "cakeish" way:
class Item extends AppModel
{
/* snip */
var $virtualFields = array('item_count' => 'count(Item.id)');
function getCountForSection($sectionId)
{
$ca = $this->Catalogitem->find
(
'all',
array
(
'fields' => array('Catalogitem.id'),
'conditions' => array('Catalogitem.section_id' => $sectionId),
'recursive' => -1
)
);
$ca = Set::extract('/Catalogitem/id', $ca);
$ret = $this->find
(
'all',
array
(
'fields' => array('Item.catalogitem_id', 'item_count'),
'conditions' => array('Item.catalogitem_id' => $ca),
'group' => array('Item.catalogitem_id'),
'recursive' => -1
)
);
return $ret;
}
}
Then simply use it in your controller:
$ret = $this->Item->getCountForSection(1);
debug($ret);
How does it work:
Define a virtual field (cake 1.3+ only AFAIK) which will count items
Fetch all the Catalogitems belonging to a Section you're interested in
Use Set::extract() to get the Catalogitems in a simple array
Use the array of Catalogitems to filter Items while counting and grouping them
NB: You don't seem to be using Cake's naming conventions in your database. This may hurt you.
Sorry, in my first answer I somehow missed your GROUP BY requirement, which was the whole point of the question, I now realize. I haven't used this yet, but I came across it recently, and it looks like it might accomplish what you are looking for: Linkable Behavior.
http://planetcakephp.org/aggregator/items/891-linkable-behavior-taking-it-easy-in-your-db
Like Containable, but works with only right and left joins, produces much more compact queries and supports GROUP BY.
http://github.com/rafaelbandeira3/linkable
#azv
Would this work for you:
$section_id = 5;
$fields = array('CatalogItem.id as CatalogItemId', 'count(*) AS SectionCount');
$conditions = array('Section.id' => $section_id);
$joins = array(
array('table' => 'catalogitem',
'alias' => 'CatalogItem',
'type' => 'LEFT',
'conditions' => array('Section.id' => 'CatalogItem.section_id')
),
array('table' => 'item',
'alias' => 'Item',
'type' => 'LEFT',
'conditions' => array('Item.catalogitem_id' => 'CatalogItem.id')
));
$data = $this->Section->find('all',
array('fields' => $fields,
'conditions' => $conditions,
'joins' => $joins,
'group' => 'CatalogItem.id',
'recursive' => -1)
);
// access your data values
foreach ($data['Section'] as $i => $datarow) {
$catalogitem_id = $datarow['CatalogItemId'];
$section_count = $datarow['SectionCount'];
}
This way you are explicitly setting your joins and doing it all in one query. See here for more info on joins in Cake:
http://book.cakephp.org/view/1047/Joining-tables
Hope this helps. All the best,
-s_r