php - How to split an XML file into three new arrays based on one parameter?

Solution:
I did something quick here, that's actually not very beautiful, but it may help.
<?php
class School {
public $gradeRange;
public $name;
public $address;
public $city;
public $high;
public function __construct($gradeRange, $name, $address, $city) {
$this->gradeRange = $gradeRange;
$this->name = $name;
$this->adress = $address;
$this->city = $city;
}
}
$data = array(
new School(112, "springfield hs", "99 strt", "Springfield"),
new School(111, "stuff hs", "blvd stuff", "stufftown"),
new School(134, "univerisity (not an highschool)", "here", "charlestown"),
new School(110, "paul", "23 rd", "poll"),
);
foreach ($data as $school) {
if (preg_match("/1[0-2]$/", $school->gradeRange)) { ?>
<tr>
<td><?= $school->name ?></td>
<td>
<address><?= $school->address ?>, <?= $school->city ?></address>
</td>
</tr>
<?php }
}
class SchoolSorter
{
public $schools;
/**
* @param array|School $schools
*/
public function __construct(array $schools)
{
//$xmlReadre = new XMLReader();
//$xmlReadre->readString($xml);
$this->schools = $schools;
}
public function sortSchools()
{
foreach ($this->schools as $school) {
if (preg_match("/1[0-2]$/", $school->gradeRange)) {
$school->high = "true";
}
}
}
}
$that_scholl_sorter = new SchoolSorter(
$data
);
echo "before: \n";
var_dump($that_scholl_sorter->schools);
$that_scholl_sorter->sortSchools();
echo "after: \n";
var_dump($that_scholl_sorter->schools);
which outputs like this
<tr>
<td>springfield hs</td>
<td>
<address>, Springfield</address>
</td>
</tr>
<tr>
<td>stuff hs</td>
<td>
<address>, stufftown</address>
</td>
</tr>
<tr>
<td>paul</td>
<td>
<address>, poll</address>
</td>
</tr>
before:
array(4) {
[0]=>
object(School)#1 (6) {
["gradeRange"]=>
int(112)
["name"]=>
string(14) "springfield hs"
["address"]=>
NULL
["city"]=>
string(11) "Springfield"
["high"]=>
NULL
["adress"]=>
string(7) "99 strt"
}
[1]=>
object(School)#2 (6) {
["gradeRange"]=>
int(111)
["name"]=>
string(8) "stuff hs"
["address"]=>
NULL
["city"]=>
string(9) "stufftown"
["high"]=>
NULL
["adress"]=>
string(10) "blvd stuff"
}
[2]=>
object(School)#3 (6) {
["gradeRange"]=>
int(134)
["name"]=>
string(31) "univerisity (not an highschool)"
["address"]=>
NULL
["city"]=>
string(11) "charlestown"
["high"]=>
NULL
["adress"]=>
string(4) "here"
}
[3]=>
object(School)#4 (6) {
["gradeRange"]=>
int(110)
["name"]=>
string(4) "paul"
["address"]=>
NULL
["city"]=>
string(4) "poll"
["high"]=>
NULL
["adress"]=>
string(5) "23 rd"
}
}
after:
array(4) {
[0]=>
object(School)#1 (6) {
["gradeRange"]=>
int(112)
["name"]=>
string(14) "springfield hs"
["address"]=>
NULL
["city"]=>
string(11) "Springfield"
["high"]=>
string(4) "true"
["adress"]=>
string(7) "99 strt"
}
[1]=>
object(School)#2 (6) {
["gradeRange"]=>
int(111)
["name"]=>
string(8) "stuff hs"
["address"]=>
NULL
["city"]=>
string(9) "stufftown"
["high"]=>
string(4) "true"
["adress"]=>
string(10) "blvd stuff"
}
[2]=>
object(School)#3 (6) {
["gradeRange"]=>
int(134)
["name"]=>
string(31) "univerisity (not an highschool)"
["address"]=>
NULL
["city"]=>
string(11) "charlestown"
["high"]=>
NULL
["adress"]=>
string(4) "here"
}
[3]=>
object(School)#4 (6) {
["gradeRange"]=>
int(110)
["name"]=>
string(4) "paul"
["address"]=>
NULL
["city"]=>
string(4) "poll"
["high"]=>
string(4) "true"
["adress"]=>
string(5) "23 rd"
}
}
In your example, you were using$this->high = "true";
, but you probably meant$school->high = "true";
.
It's not splitting data based on the match, but you could loop on an array of regular expressions and pass the regex as a parameter tosortSchools($regex)
, do the preg_match on that parameter and have the function return an array of matching schools.
No dumb question here ;).
Instead ofpreg_match
, you could also usepreg_grep
to get the indexes of the matching schools in an array
preg_grep("/1[0-2]$/", explode("\n", $input_lines));
. You may find this php live regex tester useful.
Note that you could also pass schools by reference, see PHP Documentation, but that is not recommended, see this other question.
Answer
Solution:
Looking at the first code-example in your question:
foreach ($data as $school) {
if (preg_match("/1[0-2]$/", $school->gradeRange)) {?>
<tr><td><?=$school->name?></td>
<td><address><?=$school->address?>, <?=$school->city?></address></td>
You have a typical filter-condition here with yourif
clause.
You now want to move it outside of the template and more inside$data
.
As$data
is a simplexml element (I guess) and you haven't shared any details of the School class you've been written, I actually can't tell you how you finally move it into that class, however you can easily create yourself a that is able to work on a more specific SimpleXMLElement: (or you can wrap the existing element once.
Such a filter is easy to implement:
class HighSchools extends FilterIterator
{
public function accept()
{
$school = $this->getInnerIterator()->current();
return preg_match("/1[0-2]$/", $school->gradeRange);
}
}
This on it's own (given you instantiate a SimpleXMLIterator instead of a SimpleXMLIterator which should work w/o problems) allows you to move the filter-condition out-of theforeach
:
$highSchools = new HighSchools($data);
foreach ($highSchools as $school) {
echo $school->name, "\n";
}
As it's likely you won't have a single filter, you can create a family of filter classes so that's easier to write more than one filter and reducing the duplicate code:
abstract class SchoolFilter extends FilterIterator
{
final public function accept()
{
$school = $this->getInnerIterator()->current();
return $this->acceptSchool($school);
}
}
class HighSchools extends SchoolFilter
{
public function acceptSchool($school)
{
return preg_match("/1[0-2]$/", $school->gradeRange);
}
}
You can then move it on inside the school class. It's even possible to extend from SimpleXMLIterator and make a special one that is able to offer accessor methods to filtered collections of itself.
Another alternative would be that you off the classification / typification of the schools inside the XML already so that you can as well easily query the document with xpath.
I hope this offers some paths to look into towards a more modular design so that it's easier to you to find places where to put the filtering conditions as well as make them interchangeable.
Because you don't need three arrays here. You're just looking for displaying three times the same data but just in a different fashion. The data is there just once - not three times. Keep it that simple then you can do a thousand different ways to present that data with little code modifications.
Share solution ↓
Additional Information:
Link To Answer People are also looking for solutions of the problem: attempt to read property "id" on null
Didn't find the answer?
Our community is visited by hundreds of web development professionals every day. Ask your question and get a quick answer for free.
Similar questions
Find the answer in similar questions on our website.
Write quick answer
Do you know the answer to this question? Write a quick response to it. With your help, we will make our community stronger.