你已經很遠了,你已經很好地分析了你需要處理的數據。另外你如何說你想分析數據看起來非常適合我。唯一可能會有所改善的是你要小心,不要一次做太多。
這樣做的一種方法是將問題分成更小的問題。我將告訴你如何將代碼放入多個函數和方法。但是讓我們從一個單一的函數開始,這是一步一步的,所以你可以試着按照例子來構建它。
在PHP中分離問題的一種方法是使用函數。例如,寫一個函數在XML文檔中進行搜索,這使得代碼看起來更美好,更講:
/**
* search metadata element
*
*
* @param SimpleXMLElement $xml
* @param string $resource metadata attribute
* @param string $lookup metadata attribute
* @param string $value search value
*
* @return SimpleXMLElement
*/
function metadata_search(SimpleXMLElement $xml, $resource, $lookup, $value) {
$xpath = "//METADATA[@Resource='{$resource}' and @Lookup='{$lookup}']"
."/DATA[contains(., '{$find}')]";
list($element)= $xml->xpath($xpath);
return $element;
}
所以,現在你可以輕鬆地搜索文檔,參數被命名並記錄在案。所有需要的是調用函數並獲取返回值:
$data = metadata_search($xml, 'Property', 'Area', 2);
這可能不是一個完美的函數,但它已經是一個例子。在函數旁邊還可以創建對象。對象是具有自己的上下文的函數。這就是爲什麼這些函數被稱爲方法,然後,它們屬於該對象。類似於SimpleXMLElement的xpath()
方法。
如果看到上述功能,則第一個參數是$xml
對象。然後執行xpath方法。最後,這個函數真正做的是根據輸入變量創建和運行xpath查詢。
如果我們可以直接將該函數轉換成$xml
對象,那麼我們不需要再將此作爲第一個參數傳遞。這是下一步,它的工作原理是延伸SimpleXMLElement
。我們只是添加一個新的方法,搜索和方法幾乎與上面相同。我們也從SimpleXMLElement
延長,這意味着我們將創建一個子類的吧:這是所有它已經加上新的方法添加:
class MetadataElement extends SimpleXMLElement
{
/**
* @param string $resource metadata attribute
* @param string $lookup metadata attribute
* @param string $value search value
*
* @return SimpleXMLElement
*/
public function search($resource, $lookup, $value) {
$xpath = "//METADATA[@Resource='{$resource}' and @Lookup='{$lookup}']"
."/DATA[contains(., '{$value}')]";
list($element)= $this->xpath($xpath);
return $element;
}
}
爲了得到這個生命,我們需要提供的這個名字類加載XML字符串時。那麼搜索方法可以直接調用:
$xml = simplexml_load_string($xmlString, 'MetadataElement');
$data = $xml->search('Property', 'Area', 2);
瞧,搜索現在是與爲SimpleXMLElement!
但是這個怎麼辦$data
?它只是一個XML元素,它仍然包含選項卡。
更糟的是,上下文丟失:這屬於哪個元數據列?那是你的問題。所以我們需要接下來解決這個問題 - 但是如何?
老實說,有很多方法可以做到這一點。我有一個想法是創建一個表對象了基於元數據元素的XML的:
即使有很好的調試輸出
list($metadata) = $xml->xpath('//METADATA[1]');
$csv = new CsvTable($metadata);
echo $csv;
:
+---------+----------+-----+
|LongValue|ShortValue|Value|
+---------+----------+-----+
|Salado |Sal |5 |
+---------+----------+-----+
|Academ |Aca |2 |
+---------+----------+-----+
|Rogers |Rog |1 |
+---------+----------+-----+
|Bartlett |Bar |4 |
+---------+----------+-----+
但是,這是不知何故,如果你大量的工作可能不會流暢地使用編程對象,所以自己構建整個表模型可能有點多。
因此,我有這樣的想法:爲什麼不繼續使用已經使用的XML對象,並在那裏更改XML以使其更好地滿足您的需求。來源:
<METADATA Resource="Property" Lookup="Area">
<COLUMNS> LongValue ShortValue Value </COLUMNS>
<DATA> Salado Sal 5 </DATA>
要:
<METADATA Resource="Property" Lookup="Area" transformed="1">
<COLUMNS> LongValue ShortValue Value </COLUMNS>
<DATA>
<LongValue>Salado</LongValue><ShortValue>Sal</ShortValue><Value>5</Value>
</DATA>
這將允許不僅每一個具體列名搜索也找到了數據元素的其他值。如果搜索返回$data
元素:
$xml = simplexml_load_string($xmlString, 'MetadataElement');
$data = $xml->search('Property', 'Area', 5);
echo $data->Value; # 5
echo $data->LongValue; # Salado
如果我們把與元數據元素的附加屬性,而我們的搜索,我們可以將這些元素。如果找到一些數據並且元素尚未轉換,它將被轉換。
因爲我們都在做這個裏面的搜索方法,所以使用搜索方法的代碼一定不能改變太多(即使根本就沒有改變 - 依賴於你有的詳細需求,我可能沒有完全掌握那些,但我認爲你明白了)。所以讓我們把這個工作。因爲我們不想一下子做到這一點,我們創建了多種新的方法:
- 變換元數據元素
- 搜索原來的元素中(此代碼,我們已經,我們只是將它)
一路上我們也將創造我們認爲有用的方法,順便說一下,你會發現,這也是部分代碼,您已經(在搜索()等)已經寫的,它只是放在現在內的$xml
對象 - 它更自然地屬於它。
然後最後這些新方法將放在現有的search()
方法中。
所以首先,我們創建一個輔助方法來將這個標籤行解析成一個數組。這基本上是你的代碼,你不需要在trim
前面投射的字符串,這是唯一的區別。因爲這個功能只在裏面需要,所以我們把它設爲私人的:
private function asExplodedString() {
return explode("\t", trim($this));
}
它的名字很清楚它的作用。它給出了本身的tab-exploded陣列。如果你還記得,我們在$xml
之內,所以現在每個xml元素都有這個方法。如果你不完全明白這一點呢,只是去上,你可以看到它是如何工作的正下方,我們只增加一個方法作爲輔助:
public function getParent() {
list($parent) = $this->xpath('..') + array(0 => NULL);
return $parent;
}
此功能允許我們檢索的父元素元件。這很有用,因爲如果我們找到一個數據元素,我們想要轉換父元數據元素。並且因爲這個功能是通用的,所以我選擇了public。所以它也可以在外部代碼中使用。它解決了一個共同的問題,因此不像爆炸方法那樣具有特定的性質。
所以現在我們要轉換一個元數據元素。雖然上面這兩個輔助方法需要更多的代碼,但由於這些方法並不複雜。
我們假設這個方法被調用的元素是元數據元素。我們不會在這裏添加支票以保持代碼小。由於這是一個私有函數,我們甚至不需要檢查:如果在錯誤的元素上調用此方法,則錯誤在類內部完成 - 而不是從外部代碼執行。這也是爲什麼我在這裏使用私有方法的一個很好的例子,它更具體。
所以我們現在對元數據元素所做的事實際上很簡單:我們獲取列元素,爆炸列名,然後我們遍歷每個數據元素,爆炸數據,然後清空數據-element僅用於將列名稱的子項添加到它。最後,我們添加一個屬性來標記要轉換的元素:
private function transform() {
$columns = $this->COLUMNS->asExplodedString();
foreach ($this->DATA as $data) {
$values = $data->asExplodedString();
$data[0] = ''; # set the string of the element (make <DATA></DATA> empty)
foreach ($columns as $index => $name) {
$data->addChild($name, $values[$index]);
}
}
$this['transformed'] = 1;
}
好的。現在是什麼給了?我們來測試一下。要做到這一點,我們修改現有的搜索功能返回轉換後的數據元素 - 通過添加一行代碼:
public function search($resource, $lookup, $value) {
$xpath = "//METADATA[@Resource='{$resource}' and @Lookup='{$lookup}']"
. "/DATA[contains(., '{$value}')]";
list($element) = $this->xpath($xpath);
$element->getParent()->transform();
###################################
return $element;
}
然後我們輸出爲XML:
$data = $xml->search('Property', 'Area', 2);
echo $data->asXML();
現在,這使下面的輸出(美化,它是在一個單一的線路正常):
<DATA>
<LongValue>Academ</LongValue>
<ShortValue>Aca</ShortValue>
<Value>2</Value>
</DATA>
而且我們也檢查新的屬性設置,並且元數據表/塊的所有其它數據元被變換還有:
echo $data->getParent()->asXML();
和輸出(美化),以及:
<METADATA Resource="Property" Lookup="Area" transformed="1">
<COLUMNS> LongValue ShortValue Value </COLUMNS>
<DATA>
<LongValue>Salado</LongValue>
<ShortValue>Sal</ShortValue>
<Value>5</Value>
</DATA>
...
這表明代碼按預期工作。這可能已經解決了你的問題。例如。如果您總是搜索一個數字,而其他列不包含數字,並且您只需要搜索每個元數據塊一個。但可能不是,因此需要更改搜索功能以在內部執行正確的搜索和轉換。
這次我們再次利用$this
將方法放在具體的XML元素上。兩個新methhods:一個是基於它的屬性得到一個元數據元素:
private function getMetadata($resource, $lookup) {
$xpath = "//METADATA[@Resource='{$resource}' and @Lookup='{$lookup}']";
list($metadata) = $this->xpath($xpath);
return $metadata;
}
另一個搜索元數據元素的特定列:
private function searchColumn($column, $value) {
return $this->xpath("DATA[{$column}[contains(., '{$value}')]]");
}
這兩種方法在主,然後使用搜索方法。首先通過其屬性查找元數據元素,會稍微改變它。然後將檢查是否需要轉換,然後通過值列進行搜索:
public function search($resource, $lookup, $value)
{
$metadata = $this->getMetadata($resource, $lookup);
if (!$metadata['transformed']) {
$metadata->transform();
}
list($element) = $metadata->searchColumn('Value', $value);
return $element;
}
現在新的搜索方式終於完成了。現在只搜索在右列和改造將在飛行來完成:
$xml = simplexml_load_string($xmlString, 'MetadataElement');
$data = $xml->search('Property', 'Area', 2);
echo $data->LongValue, "\n"; # Academ
現在看起來不錯,看起來,就好像它是完全好用!所有的複雜性都進入了MetadataElement。它看起來如何一目瞭然?
/**
* MetadataElement - Example for extending SimpleXMLElement
*
* @link http://stackoverflow.com/q/16281205/367456
*/
class MetadataElement extends SimpleXMLElement
{
/**
* @param string $resource metadata attribute
* @param string $lookup metadata attribute
* @param string $value search value
*
* @return SimpleXMLElement
*/
public function search($resource, $lookup, $value)
{
$metadata = $this->getMetadata($resource, $lookup);
if (!$metadata['transformed']) {
$metadata->transform();
}
list($element) = $metadata->searchColumn('Value', $value);
return $element;
}
private function getMetadata($resource, $lookup) {
$xpath = "//METADATA[@Resource='{$resource}' and @Lookup='{$lookup}']";
list($metadata) = $this->xpath($xpath);
return $metadata;
}
private function searchColumn($column, $value) {
return $this->xpath("DATA[{$column}[contains(., '{$value}')]]");
}
private function asExplodedString() {
return explode("\t", trim($this));
}
public function getParent() {
list($parent) = $this->xpath('..') + array(0 => NULL);
return $parent;
}
private function transform() {
$columns = $this->COLUMNS->asExplodedString();
foreach ($this->DATA as $data) {
$values = $data->asExplodedString();
$data[0] = ''; # set the string of the element (make <DATA></DATA> empty)
foreach ($columns as $index => $name) {
$data->addChild($name, $values[$index]);
}
}
$this['transformed'] = 1;
}
}
也不錯。許多隻有一小段代碼的小方法,即(rel。)易於遵循!
所以我希望這給了一些靈感,我知道這是一個相當一些文字閱讀。玩的開心!