So I have been asking a lot of array based questions as I strive to understand PHP's main source of data structure.
I am currently working on building a class that spits out a list of artists and their songs. each third song has a date beside it. as seen in this array:
$music = array(
'Creed' => array(
'Human Clay' => array(
array(
'title' => 'Are You Ready'
),
array(
'title' => 'What If'
),
array(
'title' => 'Beautiful',
'date' => '2012'
),
array(
'title' => 'Say I'
),
array(
'title' => 'Wrong Way'
),
array(
'title' => 'Faceless Man',
'date' => '2013'
),
array(
'title' => 'Never Die'
),
array(
'title' => 'With Arms Wide pen'
),
array(
'title' => 'Higher',
'date' => '1988'
),
array(
'title' => 'Was Away Those Years'
),
array(
'title' => 'Inside Us All'
),
array(
'title' => 'Track 12',
'date' => '1965'
),
),
),
);
What I wrote was the following:
class Music{
protected $_music = array();
protected $_html = '';
public function __construct(array $music){
$this->_music = $music;
}
public function get_music(){
$year = '';
$this->_html .= '<d1>';
foreach($this->_music as $artist=>$album){
$this->_html .= '<dt>' . $artist . '</dt>';
foreach($album as $track=>$song){
foreach($song as $songTitle){
if(isset($songTitle['date']) && !empty($songTitle['date'])){
$year = '['.$songTitle['date'].']';
}
$this->_html .= '<dd>' . $songTitle['title'] . $year. '</dd>';
}
}
}
$this->_html .= '</d1>';
}
public function __toString(){
return $this->_html;
}
}
$object = new Music($music);
$object->get_music();
echo $object;
My question is that I end up getting something back that looks like this:
Creed
Are You Ready
What If
Beautiful[2012]
Say I[2012]
Wrong Way[2012]
Faceless Man[2013]
Never Die[2013]
With Arms Wide pen[2013]
Higher[1988]
Was Away Those Years[1988]
Inside Us All[1988]
Track 12[1965]
As you can see almost every song has a date beside it when in the array that is not the case. My question is whats the deal? I thought in my loop I was very clear in stating if this song has a year, set it and then print it beside the song title?
Can some one point me in the right direction please?
When the date is not set for a song, the year variable is holding the value from the previous song (for which it was set). You are still actually telling it to print year next to the title, whether or not the date is set.
You need an else clause on the if:
if(isset($songTitle['date']) && !empty($songTitle['date'])){
$year = '['.$songTitle['date'].']';
} else {
$year = '';
}
This will clear out year when the date is not set.
You never reset $year after you set it, so once you encounter the first year value in your array, you'll be constantly using that same year until a new one comes along:
if(isset($songTitle['date']) && !empty($songTitle['date'])){
$year = '['.$songTitle['date'].']';
} else {
$year = ''; /// reset year to blank
}
And in somewhat unrelated stuff, this is probably a typo:
$this->_html .= '<d1>';
^--- number one? not letter L for a `<dl>` tag?
You are not resetting year in foreach loop, so it is using last set year until value is reassigned. Here is corrected Music class.
class Music{
protected $_music = array();
protected $_html = '';
public function __construct(array $music){
$this->_music = $music;
}
public function get_music(){
$year = '';
$this->_html .= '<d1>';
foreach($this->_music as $artist=>$album){
$this->_html .= '<dt>' . $artist . '</dt>';
foreach($album as $track=>$song){
foreach($song as $songTitle){
if(isset($songTitle['date']) && !empty($songTitle['date'])){
$year = '['.$songTitle['date'].']';
}
$this->_html .= '<dd>' . $songTitle['title'] . $year. '</dd>';
$year = "";
}
}
}
$this->_html .= '</d1>';
}
public function __toString(){
return $this->_html;
}
}
Related
I am trying to save a custom post type. I can't figure out why the save isn't working for the input fields listed below.
I included all the relevant code, as I can't see where the problem might be. It seems like everything is working except for the save function (listed last).
I am only trying to save inputs where 'type' => 'singleinput'. I haven't tried to save the 'fulltext' ones.
Can anyone point out where I am going wrong?
//input boxes for custom post
$specialpage->inputs = array(
'item1' => array(
'type' => 'singleinput',
'headline' => 'Page headline',
'bullet1' => 'Bullet Point 1:',
'bullet2' => 'Bullet Point 2:',
'bullet3' => 'Bullet Point 3:'
),
'item2' => array(
'type' => 'fulltext',
'promo1' => 'Promo text 1:',
'promo2' => 'Promo text 2:',
'promo3' => 'Promo text 3:'
),
'item3' => array(
'type' => 'singleinput',
'quote' => 'The quote'
)
);
This function is called to display the meta box.
public function display_meta_box_($post_object){
foreach($this->inputs as $item){
foreach($item as $name => $title){
if ($title == 'singleinput'){
$activate = 'singleinput';
continue;
}
if ($title == 'fulltext'){
$activate = 'fulltext';
continue;
}
if ($activate == 'singleinput'){
$this->singleinput[$name] = $title;
${$this->name . $name} = get_post_meta($post_object->ID, $this->name . $name, true);
echo '<span class="meta_titles">' . $title . '</span>';
?>
<input type='text' class='input_single' name='<?php echo $this->name . $name;?>_name' value='<?php echo ${$this->name . $name}; ?>' >
<?php
}
}
This is the save function
public function save_profile( $post_id, $post_object ) {
if( $this->name == $post_object->post_type){
//check if singleinput are defined for this custom post type
if ($this->singleinput){
//save single line text input boxes
foreach ($this->singleinput as $name => $title){
update_post_meta($post_id, $this->name . $name, $_POST[$this->name . $name . '_name']);
}
}
}
Edit: Added Save Function
$simplejoiner->saveNow();
public function saveNow(){
add_action( 'save_post', array($this, 'save_profile'), 10, 2 );
}
This is my simple looper code
foreach( $cloud as $item ) {
if ($item['tagname'] == 'nicetag') {
echo $item['tagname'];
foreach( $cloud as $item ) {
echo $item['desc'].'-'.$item['date'];
}
} else
//...
}
I need to use if method in this looper to get tags with same names but diferent descriptions and dates. The problem is that I dont know every tag name becouse any user is allowed to create this tags.
Im not really php developer so I'm sory if it's to dummies question and thanks for any answers!
One possible solution is to declare a temporary variable that will hold tagname that is currently looped through:
$currentTagName = '';
foreach( $cloud as $item ) {
if ($item['tagname'] != $currentTagName) {
echo $item['tagname'];
$currentTagName = $item['tagname'];
}
echo $item['desc'] . '-' . $item['date'];
}
I presume that your array structure is as follows:
$cloud array(
array('tagname' => 'tag', 'desc' => 'the_desc', 'date' => 'the_date'),
array('tagname' => 'tag', 'desc' => 'the_desc_2', 'date' => 'the_date_2'),
...
);
BUT
This solution raises a problem - if your array is not sorted by a tagname, you might get duplicate tagnames.
So the better solution would be to redefine your array structure like this:
$cloud array(
'tagname' => array (
array('desc' => 'the_desc', 'date' => 'the_date'),
array('desc' => 'the_desc_2', 'date' => 'the_date_2')
),
'another_tagname' => array (
array('desc' => 'the_desc_3', 'date' => 'the_date_3'),
...
)
);
and then you can get the data like this:
foreach ($cloud as $tagname => $items) {
echo $tagname;
foreach($items as $item) {
echo $item['desc'] . '-' . $item['date'];
}
}
I have this method:
private function formatCliendCardData($data) {
$formatedData = array();
$formatedData['first_name'] = trim($data['name']);
$formatedData['last_name'] = trim($data['surname']);
$formatedData['date_of_birth'] = $data['birth_year'] . '-' . sprintf("%02d", $data['birth_month_id']) . '-' . sprintf("%02d", $data['birth_day']); //[yyyy-mm-dd]
$formatedData['sex'] = ($data['sex_id'] == 's1'? 'm' : 'f');
$formatedData['county'] = 'Latvija'; //#TODO get real data
$formatedData['post_index'] = (int) preg_replace( '/[^0-9]/', '', $data['post_index']);
$formatedData['city'] = trim($data['city']);
$formatedData['street'] = trim($data['street']);
$formatedData['house_nr'] = trim($data['house_nr']);
$formatedData['phone'] = trim($data['phone']);
$formatedData['email'] = trim($data['email']);
return $formatedData;
}
I run in this problem from time to time. I have big method that just reformats data and returns it. It looks ugly. There is few other aproaches Im aware -- just make foreach loop, but there are exeptions, so i need make 'ifs' anyway. What is better way to reformat such data in your opinon?
I aggree with helloei,
create a class to handle all the formating and validation in the setter.
class Person
{
public function setName($name)
{
$this->name = trim($name);
}
...
}
and then create the object in your function:
private function formatCliendCardData($data) {
$person = new Person();
$person->setName($data['name`])
...
if you have highly custom condition to reformat some datas i think you don't have any other solution that apply this condition manually with some if/else or other custom loop.
if else, you have some unified condition to reformat you can aggregate this and apply in batch at your datas. In other word for my opinion the only other solution are: generalize conditions of your reformatting operations and apply this at your data.
this is Object Oriented approach
IMHO In those cases you have to reduce and clean your code. Often I use an helper function that allows me to split data and formatting rules.
// helper function (OUTSITE YOUR CLASS)
function waterfall($input=NULL,$fns=array()){
$input = array_reduce($fns, function($result, $fn) {
return $fn($result);
}, $input);
return $input;
}
// your formatting rules
$rules = array(
'first_name' => array('trim'),
'last_name' => array('trim'),
'date_of_birth' => array(
'f0' => function($x){
return sprintf("%s-%02d-%02d",
$x['birth_year'],
$x['birth_month_id'],
$x['birth_day']
);
},
'trim'
),
'sex' => array(
'f0' => function($x){
if($x['sex_id'] == 's1'){
return 'm';
}else{
return 'f';
}
}
),
'post_index' => array(
'f0' => function($x){
return (int) preg_replace('/[^0-9]/', NULL, $x);
},
'trim'
),
'city' => array('trim'),
'email' => array('strtolower','trim')
);
// your data
$data = array(
'first_name' => ' Andrea',
'last_name' => 'Ganduglia ',
'date_of_birth' => array(
'birth_year' => '1899',
'birth_month_id' => 5,
'birth_day' => 7
),
'post_index' => 'IT12100',
'city' => 'Rome',
'email' => 'USER#DOMAIN.tld '
);
// put all together
$formatedData = array();
foreach($data as $k => $v){
$formatedData[$k] = waterfall($v,$rules[$k]);
}
// results
print_r($formatedData);
/*
Array
(
[first_name] => Andrea
[last_name] => Ganduglia
[date_of_birth] => 1899-05-07
[post_index] => 12100
[city] => Rome
[email] => user#domain.tld
)
*/
I'm trying to pass variable to the view and this one is very weird as the naming and directory structure is correct. Below is the function in my controller:
public function validate_apply_link(){
App::uses('CakeEmail', 'Network/Email');
$this->layout = 'blank';
$listings = $this->CareersAndJob->query("
SELECT l.sid, l.title, lp.value, u.CompanyName, u.WebSite
FROM listings l
LEFT JOIN listings_properties lp
ON lp.object_sid = l.sid
LEFT JOIN users u
ON u.sid = l.user_sid
WHERE l.active = 1
AND lp.add_parameter = 2
AND l.JobGateSenderReference IS NULL
AND u.CompanyName != 'AECOM'
ORDER BY u.CompanyName ASC
LIMIT 5
");
$doc = new DOMDocument();
ob_start();
$listing_count = count($listings);
echo nl2br("Checking $listing_count active jobs...\n\n");
$i=0;
foreach($listings as $listing){
$sid = $listing['l']['sid'];
$url = $listing['lp']['value'];
$company_name = $listing['u']['CompanyName'];
$title = htmlspecialchars($listing['l']['title']);
$length = strpos($title, "-");
if($length != 0){
$title = substr($title, 0, $length-1);
}
$title = substr($title, 0, $length-1);
$title = substr($title, 0, 10);
$data = $this->curl($url);
$check_pdf = strpos($data['info']['content_type'], "pdf");
if($check_pdf != false){
$outputs['data'][$i]['url'] = $url;
$outputs['data'][$i]['sid'] = $sid;
$outputs['data'][$i]['title'] = $title;
$outputs['data'][$i]['company_name'] = $company_name;
$outputs['data'][$i]['our_link'] = "http://careersandjobs.com.au/display-job/{$sid}";
$outputs['data'][$i]['content_type'] = $data['info']['content_type'];
$outputs['data'][$i]['data_type'] = 'pdf';
$i++;
continue;
}
#$doc->loadHTML($data['results']);
$html = $doc->saveHTML();
$xpath = new DOMXpath($doc);
$body = $doc->getElementsByTagName('body')->item(0);
$parsed_url = parse_url($url);
switch($parsed_url['host']){
case "www.michaelpage.com.au":
parse_str($url);
$exist = $xpath->query("//*[contains(#value,'{$ref}')]");
break;
case "https://vacancies.mackay.qld.gov.au":
parse_str($url);
$exist = $xpath->query("//*[contains(#value,'{$title}')]");
break;
default:
$exist = $xpath->query("//*[contains(text(),'{$title}')]");
break;
}
if($exist->length == 0){
if(strpos($url, '#') == false){
$outputs['data'][$i]['url'] = $url;
$outputs['data'][$i]['sid'] = $sid;
$outputs['data'][$i]['title'] = $title;
$outputs['data'][$i]['company_name'] = $company_name;
$outputs['data'][$i]['our_link'] = "http://careersandjobs.com.au/display-job/{$sid}";
$outputs['data'][$i]['content_type'] = $data['info']['content_type'];
$response_code = $this->http_response_codes($data['info']['http_code']);
$outputs['data'][$i]['response_code'] = $response_code;
$outputs['data'][$i]['data_type'] = 'title_not_found';
}else{
$outputs['data'][$i]['data_type'] = 'no_iframe';
}
$i++;
}
flush();
ob_flush();
}
$this->set(compact('outputs'));
}
I can do pr on the outputs variable in the view but this outputs to NULL but when I delete the entire bunch of code inside the controller function and just pass a test variable through it works.
Is there something wrong with the function that I am not aware of?
No errors were found in the above function by the way
app/Controller/CareersAndJobsController.php (line 1048)
array(
'data' => array(
(int) 0 => array(
'url' => 'http://bawbawshire.currentjobs.com.au/cvbuilder/apply+for+this+job/no/1225055',
'sid' => '3649',
'title' => 'Graduate P',
'company_name' => 'Baw Baw Shire Council',
'our_link' => 'http://careersandjobs.com.au/display-job/3649',
'content_type' => 'text/html; charset=utf-8',
'response_code' => 'OK',
'data_type' => 'title_not_found'
),
(int) 1 => array(
'url' => 'http://bawbawshire.currentjobs.com.au/cvbuilder/apply+for+this+job/no/1225724',
'sid' => '3726',
'title' => 'Program &a',
'company_name' => 'Baw Baw Shire Council',
'our_link' => 'http://careersandjobs.com.au/display-job/3726',
'content_type' => 'text/html; charset=utf-8',
'response_code' => 'OK',
'data_type' => 'title_not_found'
),
(int) 2 => array(
'url' => 'http://bawbawshire.currentjobs.com.au/cvbuilder/apply+for+this+job/no/1225826',
'sid' => '3727',
'title' => 'Road Netwo',
'company_name' => 'Baw Baw Shire Council',
'our_link' => 'http://careersandjobs.com.au/display-job/3727',
'content_type' => 'text/html; charset=utf-8',
'response_code' => 'OK',
'data_type' => 'title_not_found'
)
)
)
This is what I am getting from outputs variable just before it gets set by the set function in controller
Any reason you chose to use CakePHP? Because you seem to not make use of its functionality!
You're using literal SQL queries, therefore basically skipping the Models functionality.
You're outputting your content from your Controller? Be careful when using output buffering, this may conflict with CakePHP's inner workings, which also relies on output buffering in many cases. Because you're already outputting the content here (ob_flush()), you'll be outputting your content before your View is reached..
Normally I would point to specific points in the manual, however, because there's so much wrong here, I would suggest to start reading at the beginning
Basically I have 2 methods in the same class, getMovie and getGenres. They are very similar but One doesn't return what I expect.
Here's getMovie method:
public function getMovie($argType, $arg){
$movieQuery = "SELECT id,
rt_id,
imdb_id,
url,
rt_url,
type,
adult,
DATE_FORMAT(release_date, '%Y') AS year,
date_added,
title,
runtime,
budget,
revenue,
homepage,
rating,
tagline,
overview,
popularity,
image,
backdrop,
trailer
FROM movies
WHERE " . $argType . " = " . $arg;
$movieResult = $this->_query($movieQuery);
$movies = array();
if($movieResult->fetch_array(MYSQLI_ASSOC)){
while($m = $movieResult->fetch_array(MYSQLI_ASSOC)){
$movies[] = array( 'title' => $m['title'],
'duplicate' => $m['duplicate'],
'url' => $m['url'],
'rt_url' => $m['rt_url'],
'release_date' => $m['release_date'],
'date_added' => $m['date_added'],
'type' => 'movie',
'adult' => $m['adult'],
'id' => $id,
'rt_id' => $m['rt_id'],
'imdb_id' => $m['imdb_id'],
'rating' => $m['rating'],
'tagline' => $m['tagline'],
'overview' => $m['overview'],
'popularity' => $m['popularity'],
'runtime' => $m['runtime'],
'budget' => $m['budget'],
'revenue' => $m['revenue'],
'homepage' => $m['homepage'],
'image' => $m['image'],
'backdrop' => $m['backdrop'],
'trailer' => $m['trailer'] );
}
return $movies;
}
else{
return false;
}
Here's getGenres method:
public function getGenres($movieId = NULL){
$genresQuery = "";
if($movieId != NULL){
$genresQuery = "SELECT id,
name
FROM genres
WHERE id = ANY (
SELECT genre_id
FROM movie_genres
WHERE movie_id = " . $movieId . ")";
}
else{
$genresQuery = "SELECT id,
name
FROM genres";
}
$genresResult = $this->_query($genresQuery);
$genres = array();
if($genresResult->fetch_array(MYSQLI_ASSOC)){
while($genre = $genresResult->fetch_array(MYSQLI_ASSOC)){
$genres[] = array( 'id' => $genre['id'],
'name' => $genre['name'] );
}
return $genres;
}
else{
return false;
}
}
And here's how I call them:
$mov = $movie->getMovie(2207);
print_r($mov); // output: Array()
$gen = $movie->getGenres(2207);
print_r($gen); // output: Array(values inside)
Both queries do actually return expected values but getMovies method doesn't work with the if statement. It works fine if I just have while loop.
I am using if as well as while as I heard that while loop can sometimes execute even when there's not values. Is there any truth to this? If there is indeed a reason to use an if statement as well as wile loop then why doesn't it work with getMovies method?
Edit 1: I tried storing the array like so but that resulted in a memory related error:
$r = $genresResult->fetch_array(MYSQLI_ASSOC);
if($r){
while($r){
$genres[] = array( 'id' => $genre['id'],
'name' => $genre['name'] );
}
return $genres;
}
I am using if as well as while as I heard that while loop can sometimes execute even when there's not values. Is there any truth to this?
No, according to the php manual mysqli_result::fetch_array returns an array of strings that corresponds to the fetched row or NULL if there are no more rows in resultset.
Null is falsy so the while loop will not be entered.
Although the if statement is unnecessary if you had one you would use mysqli_result::$num_rows to check if the query returned any rows.
if($movieResult->num_rows > 0){
while($m = $movieResult->fetch_array(MYSQLI_ASSOC)){
...
}
}