Possible to break statement in foreach loop? - php

I have two tables (user,user_group). Where user table contains
------------+-------------+------------------+
| user_id | user_name | user_group(id) |
------------+-------------+------------------+
and user_group table contains
+-----------------+-------------------+
| user_group_id | user_group_name |
+-----------------+-------------------+
Now I try to join them without using JOIN query. To solve this problem I have used double foreach() loop, but no result returns. I can't use break statement. How can I get the desired result using a loop? The desired result would be:
+-----------+-------------+-------------------+
| user_id | user_name | user_group_name |
+-----------+-------------+-------------------+
What I have so far:
public function user_get_item()
{
$this->db->select('*');
$this->db->from('users');
$results=$this->db->get()->result_array();
$this->db->select('*');
$this->db->from('user_group');
$group_data=$this->db->get()->result_array();
foreach($results as $v_results)
{
foreach($group_data as $v_group_data)
{
if($v_results['user_group']==$v_group_data['user_group_id'])
{
$v_results['user_group']=$v_group_data['user_group_name'];
}
}
}
return $results;
}

I think what you are trying to do is changing the group id identifier by the group name in the current row if they match. So, replace:
$results['user_group']=$v_group_data['user_group_name'];
by:
$v_results['user_group']=$v_group_data['user_group_name'];
Anyway, what's wrong with joins? Making SQL queries will be always faster than a polynomic loop.

Presumably you would have this:
Array(
0 => Array(
[user_id] => 1,
[user_name] => John Doe,
[user_group] => 200
),
1 => Array(
[user_id] => 2,
[user_name] => Jane Doe,
[user_group] => 100
)
)
And:
Array(
0 => Array(
[user_group_id] => 100,
[user_group_name] => Admin
),
1 => Array(
[user_group_id] => 200,
[user_group_name] => Web User
)
)
So make a new array for the group so it looks like this (ID as key, Name as value):
Array(
[100] => Admin,
[200] => Web User
)
To do that:
public function getUserGroups()
{
$rows = array();
$this->db->select('*');
$this->db->from('user_group');
$group_data=$this->db->get()->result_array();
foreach($group_data as $results)
{
$rows[$results['user_group_id']] = $results['user_group_name'];
}
return $rows;
}
Then you just assign in the other method:
public function userGetItem()
{
# Get all the user groups
$groupData = $this->getUserGroups();
# Get your original user list
$this->db->select('*');
$this->db->from('users');
$results=$this->db->get()->result_array();
$row = array();
foreach($results as $users) {
$row[] = array(
'user_id' => $users['user_id'],
'user_name' => $users['user_name'],
# Here you assign the group name from the user groups array
'user_group_name' => $groupData[$users['user_group']]
);
}
return $row;
}
Anyway, I think JOIN would be faster/better, but this should get you the results you are looking for, though I have not tested it, it should be close.

Related

Raking Result of search query

Rank Result data set
i am building meta search tool which get results from different sources in response to user query. i have saved the results in array of object having information like title, description , release date etc.before showing it on interface i want to rank them, so that most relevant result should be on top just like search engines. but i am new to ranking and don't know about it. so kindly guide me in this matter which ranking algorithm i should follow or any useful link for help.
I think you need to add "weighting" column (can be null) on your array of Object and just before to show the elements you need to loop on weighting if they got one.
Then your results will be shown in first and if no weight just the normal display.
Here an example :
<?php
//Function to compare weightings
function cmp($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
$searches = array(
'songs'=> array(
0 => array(
'title' => 'coldplay',
'weight'=> 3
),
1 => array(
'title' => 'eminem',
'weight'=> 2
),
2 => array(
'title' => 'rihanna',
'weight'=> 2
),
3 => array(
'title' => 'shakira',
'weight'=> 1
),
4 => array(
'title' => 'nirvana',
'weight'=> null
),
5 => array(
'title' => 'acdc'
)
),
);
//this foreach is used to apply the weighting on itterations
foreach($searches['songs'] as $key => $search){
//if no weight of weight is null
if(array_key_exists('weight', $search) && $search['weight']){
$array_by_weight[$key]['weight'] = $search['weight'];
$array_by_weight[$key]['title'] = $search['title'];
}else{
$array_by_weight[$key]['weight'] = 5; //Value max of weighting
$array_by_weight[$key]['title'] = $search['title'];
}
}
//We use our function to compare and sort our array
uasort($array_by_weight, 'cmp');
//display
foreach($array_by_weight as $songs){
echo $songs['title'].' | ';
echo $songs['weight'].PHP_EOL;
}
Output :
shakira | 1
eminem | 2
rihanna | 2
coldplay | 3
acdc | 5
nirvana | 5
I hope it can help you.

Insert records to either two tables or one table depending on if a record exists or not using Laravel query builder

I'm trying to insert records to either two tables or one table depending on if a record exists or not.
First table Authors
ID | Name
1 | Joe
2 | Sam
Second table Books
ID | author_ID | Book
1 | 2 | Book1
2 | 2 | BookYYY
3 | 1 | BookABC
What I want to accomplish is to check if author exists first, if not insert author and his book and if it DOES exists insert just the book with the right author ID
Here is what I've attempted so far that doesn't seem to work.
$result = DB::table('authors')
->where('name', $data['author_name'])
->where('username', $data['author_username'])->pluck('id');
if(is_null($result)){
//Not in table add new author
$id = DB::table('authors')->insertGetId(
['name' => $data['author_name'], 'username' => $data['author_username']]
);
//Add book
DB::table('books')->insert(
['author_id' => '.$id.', 'name' => "Book777"]
);
}
else{
//Is in table insert just book
DB::table('books')->insert(
['author_id' => '.$result.', 'name' => "Book777"]
);
}
So I'm trying to add author with Book name "Book777" but if author does exists in DB get the author ID and insert just the book.
Thank you all for helping me with this! Appreciate any help.
Consider using ORM. With Eloquent you can change all your code to just this:
$author = Author::firstOrCreate(['name' => $data['author_name'], 'username' => $data['author_username']]);
$author->books()->create(['name' => 'Book777']);
With Query Builder you can do this:
$attributes = [
'name' => $data['author_name'],
'username' => $data['author_username']
];
$author = DB::table('authors')->where($attributes)->first();
$authorId = is_null($author) ? DB::table('authors')->insertGetId($attributes) : $author->id;
DB::table('books')->insert(['author_id' => $authorId, 'name' => "Book777"]);
I'm not sure if it's work or not but hope this helps
$result = DB::table('authors')
->where('name', $data['author_name'])
->where('username', $data['author_username'])->pluck('id');
if(!empty($result)){
//Is in table insert just book
DB::table('books')->insert(
['author_id' => $result, 'name' => "Book777"]
);
}
else{
//Not in table add new author
$id = DB::table('authors')->insertGetId(
['name' => $data['author_name'], 'username' => $data['author_username']]
);
//Add book
DB::table('books')->insert(
['author_id' => $id, 'name' => "Book777"]
);
}

PHP combine two arrays in foreach loop

I need guidance on how to combine two arrays in foreach loop. I have a list of checkboxes that will add user to corresponding list.
To grab my checkboxes as an array using :
<input type="checkbox" name="checkbox_list[]" value="Austin Metro">
<input type="checkbox" name="checkbox_list[]" value="Central Austin">
<input type="checkbox" name="checkbox_list[]" value="Georgetown">
...
Here is my loop:
foreach($_POST['checkbox_list'] as $check) {
$myValues = array('Austin Metro','Central Austin','Georgetown','Lake Travis | Westlake','Leander | Cedar Park','Northwest Austin','Round Rock | Pflugerville | Hutto','San Marcos | Buda | Kyle','Southwest Austin','DFW Metro','Frisco','Grapevine | Colleyville | Southlake','McKinney','Plano','Houston Metro','Conroe | Montgomery','Cy-Fair','Katy','Pearland | Friendswood','Spring | Klein','Sugar Land | Missouri City','Tomball | Magnolia','The Woodlands');
$myIds = array('3','4','5','6','7','8','9','10','11','25','12','13','14','15','24','16','17','18','19','20','21','23','22');
if (isset($check) && array_search($check, $myValues) !== false){
$index = array_search($check, $myValues);
$indeed_mail->indeed_wysija_subscribe( $myIds[$index], $email );
}
}
Currently it is adding user to the right list but is sending one email confirmation per list, instead of merging all subscribed lists in one single email.(in other words if user selects "Austin" & "Houston" lists, it will get one email for "Austin" and one email for "Houston")
Any suggestions on how to fix? Thanks!
Here is function that add subscriber and sends email confirmation:
public function indeed_wysija_subscribe( $listId, $e_mail, $first_name='', $last_name='' ){
$user_data = array(
'email' => $e_mail,
'firstname' => $first_name,
'lastname' => $last_name);
$data = array(
'user' => $user_data,
'user_list' => array('list_ids' => array($listId))
);
$helper = &WYSIJA::get('user', 'helper');
if($helper->addSubscriber($data)) return 1;
else return 0;
}
You should define the array with checkbox names/IDs outside of your loop, and use a more appropriate structure for them:
$map = array (
3 => 'Austin Metro',
4 => 'Central Austin',
5 => 'Georgetown',
6 => 'Lake Travis | Westlake',
7 => 'Leander | Cedar Park',
8 => 'Northwest Austin',
9 => 'Round Rock | Pflugerville | Hutto',
10 => 'San Marcos | Buda | Kyle',
11 => 'Southwest Austin',
25 => 'DFW Metro',
12 => 'Frisco',
13 => 'Grapevine | Colleyville | Southlake',
14 => 'McKinney',
15 => 'Plano',
24 => 'Houston Metro',
16 => 'Conroe | Montgomery',
17 => 'Cy-Fair',
18 => 'Katy',
19 => 'Pearland | Friendswood',
20 => 'Spring | Klein',
21 => 'Sugar Land | Missouri City',
23 => 'Tomball | Magnolia',
22 => 'The Woodlands',
);
Now you can even remove the loop completely and continue with this code:
// take from map only those entries that have been checked:
$checks = array_intersect($map, $_POST['checkbox_list']);
// pass the IDs of those values to the emailing class:
$indeed_mail->indeed_wysija_subscribe( array_keys($checks), $email );
You just now need to adapt that indeed_wysija_subscribe so it can deal with arrays. There is only one line to change:
'user_list' => array('list_ids' => $listId)
Notice that array($listId) becomes just $listId because that argument is now already an array.
PHP have array_combine function
http://php.net/manual/en/function.array-combine.php
Ignoring the Title of your questions and focusing on the question asked.
Currently it is adding user to the right list but is sending one email confirmation per list, instead of merging all subscribed lists in one single email.
You need to collate the user ids selected, before you pass it into the subscribe function.
Currently your calling the subscribe function, indeed_wysija_subscribe, inside the foreach loop foreach($_POST['checkbox_list'] as $check) {.
You need to move the subscribe call outside the loop and pass in an array of $listId.
This may require you to modify the indeed_wysija_subscribe function to support the array. Or there may be another function in the library.
I am not sure what is part of the library or not.
Also, you can perform the mapping between city name to numeric id in more direct manner.
$myValues = array('Austin Metro','Central Austin','Georgetown');
$myIds = array('3','4','5','6');
$cityToIdMap = array(
'Austin Metro'=>'3',
'Central Austin'=>'4',
'Georgetown'=>'5'
);
$subscriberIds = array();
foreach($_POST['checkbox_list'] as $check) {
if(!empty($check) && isset($cityToIdMap[$check])){
$subscriberIds[] = $cityToIdMap[$check];
}
}

Group results after join?

So, I have three tables. Movies, movies_genres and genres. I want to get a movie by its Id, and also join its genres in the result. I managed to join the results, but it doesn't display as i want it to. I'm not sure if what I'm asking is possible.
This is my query:
SELECT `movies`.*, GROUP_CONCAT(genres.id) AS genre_id, GROUP_CONCAT(genres.name) AS genre_name
FROM (`movies`)
INNER JOIN `movies_genres`
ON `movies_genres`.`movie_id` = `movies`.`id`
INNER JOIN `genres`
ON `genres`.`id` = `movies_genres`.`genre_id` WHERE `movies`.`id` = 19908
GROUP BY `movies`.`id`
The query was generated by Codeigniters Active Record class, here is the Codeigniter code if that helps:
$this->db->select('movies.*, GROUP_CONCAT(genres.id) AS genre_id, GROUP_CONCAT(genres.name) AS genre_name');
$this->db->from('movies');
$this->db->where('movies.id', $movie_id);
$this->db->join('movies_genres', 'movies_genres.movie_id = movies.id', 'inner');
$this->db->join('genres', 'genres.id = movies_genres.genre_id', 'inner');
$this->db->group_by('movies.id');
Here is the result i'm currently getting:
Array
(
[id] => 19908
[movie_title] => Zombieland
[overview] => An easily spooked guy...
[genre_id] => 28,12,35,27
[genre_name] => Action,Adventure,Comedy,Horror
)
And this is what I want:
Array
(
[id] => 19908
[movie_title] => Zombieland
[overview] => An easily spooked guy...
[genres] => array(
0 => array(
'id' => 28,
'name' => Action
),
1 => array(
'id' => 12,
'name' => Adventure
),
1 => array(
'id' => 35,
'name' => Comedy
),
1 => array(
'id' => 27,
'name' => Horror
)
)
)
Is this possible, and if so, how?
The query you listed will have n rows (where n = # of movies) whereas the query it seems you want will have many more rows (# of movie_genre's entries). You're probably better off leaving that query as it is, and doing some post processing.
Consider:
After you get it, just run your result (e.g. $result) array through something like:
foreach($result as &$row)
{
// Split over commas
$gi_elements = explode(',', $row['genre_id']);
$gn_elements = explode(',', $row['genre_name']);
// Build genre
$row['genre'] = array();
for($i=0; $i<count($gi_elements); $i++)
{
$row['genre'][] = array('id' => $gi_elements[$i], 'name' => $gn_elements[$i]);
}
// Cleanup
unset($row['genre_id']);
unset($row['genre_name']);
}
Afterwards, $results will look exactly as you wish without extra database work.
EDIT: Fixed some typos.

Sorting and storing array returned by a method as parent and child data for displaying nested/multi level comments in codeigniter 2.0

Hey guys I'm trying to learn codeigniter, but once again I'm STUCK and I seek help (as usual :P )
What I need to do?
-> I need to get the data related to a article from the database along with other stuff like the tags for the article and all the comments. I'm thinking of keeping single level nested comments for the article.
Well I'm done with the tag part [link to the answer which helped me with the same : Returning and using multidimensional array of records from database in CodeIgniter 2.0 ] but the comment part is driving me nuts.
Well to get started here is my comments table
Comments
+---------------+-------------+
| Field | Type |
+---------------+-------------+
| commentId | int(10) |
| PostId | int(10) |
| author | varchar(30) |
| email | varchar(30) |
| url | varchar(50) |
| date | datetime |
| comment | text |
| parent | int(10) |
+---------------+-------------+
I'm using the parent field to keep a track of the parent for a nested child comment. By default the value is 0 which means it the parent. Child comment will have the commentid of its parent comment
public function getPost($postName = NULL , $year = NULL, $month = NULL ){
if($postName != NULL && $year != NULL && $month != NULL){
//single post
$this->load->model('comment_model');
$this->db->where('postName',$postName);
$this->db->where('year(date)',$year);
$this->db->where('month(date)',$month);
$q = $this->db->get('mstack_Post');
if($q->num_rows()>0){
$post = $q->result();
foreach ($post as &$p) {
$p->tags = $this->getAllTags($p->postId);
/* getting the comments */
$com = $this->comment_model->getComments($p->postId);
/*echo count($com).' is the total count'; output= 4 */
foreach ($com as &$c) {
/* trying to filter the comment. but all I get is 1 comment as the output*/
if($c->parent==0){
$p->comments->parentComment = $c;
}elseif($c->commentId==$c->parent){
$p->comments->childComment = $c;
}
}
}
return $post;
}else{
return array();
}
}
}
Any help will surely be appreciated.
If you have any other technique /idea to display multi level comments then do let me know. :)
Here is the solution that might be helpfull:
First you need 2 helper recursive function:
// Building comments.
function buildComments($list, $parent = 0)
{
// Creating result array.
$result = array();
//looping...
foreach ($list as $item)
{
//iteration starts with 0 as default.
if ($item->parent == $parent)
{
// add to the result
$result[$item->commentId] = array(
'author' => $item->author,
// ... other definitions
'child' => buildComments($list, $item->commentId) //execute this function for child.
);
}
}
return $result;
}
function printComments($arg, $depth = 1)
{
foreach ($arg as $item)
{
// Printing comment...
echo str_repeat(' ', $depth) . $item['author'] . "<br />\r\n";
// extra echoes...
// if it has a child comment...
if (count($item['child'] > 0))
{
printComments($item['child'], $depth + 1);
}
}
}
A little explaining:
The buildComments() function will starts with rows that parents has 0. Then it will execute itself for child. if child as a child, it will add it. In the end, result will be like this:
$result = array(
1 => array(
'author' => 'John',
'child' => array(
8 => array(
'author' => 'Jane',
'child' => array(
3 => array(
'author' => 'Jamie',
'child => array()
)
)
),
6 => array(
'author' => 'Jackie',
'child => array()
),
9 => array(
'author' => 'Harry',
'child => array()
)
)
),
4 => array(
'author' => 'Jack',
'child' => array()
),
10 => array(
'author' => 'Clark',
'child' => array(
11 => array(
'author => 'Lois',
'child' => array()
)
)
),
12 => array(
'author' => 'Luthor',
'child' => array()
)
);
In the printComments() function we are printing results recursive. for each child, function repeats itself. You will get result like this:
John
Jane
Jamie
Jackie
Harry
Jack
Clark
Lois
Luthor
For more information about recursive functions Look this answer
USAGE
$this->db->where('postName',$postName);
$this->db->where('year(date)',$year);
$this->db->where('month(date)',$month);
$this->db->order_by('parent', 'asc');
$query = $this->db->get('comments');
$comments = buildComments($query->result());
printComments($comments);
that'is that simple...

Categories