[DomCrawler] Add a way to filter direct children
This commit is contained in:
parent
f0168e3ed6
commit
f634afdb6f
@ -501,16 +501,28 @@ class Crawler implements \Countable, \IteratorAggregate
|
||||
/**
|
||||
* Returns the children nodes of the current selection.
|
||||
*
|
||||
* @param string|null $selector An optional CSS selector to filter children
|
||||
*
|
||||
* @return self
|
||||
*
|
||||
* @throws \InvalidArgumentException When current node is empty
|
||||
* @throws \RuntimeException If the CssSelector Component is not available and $selector is provided
|
||||
*/
|
||||
public function children()
|
||||
public function children(/* string $selector = null */)
|
||||
{
|
||||
$selector = 0 < \func_num_args() ? func_get_arg(0) : null;
|
||||
|
||||
if (!$this->nodes) {
|
||||
throw new \InvalidArgumentException('The current node list is empty.');
|
||||
}
|
||||
|
||||
if (null !== $selector) {
|
||||
$converter = $this->createCssSelectorConverter();
|
||||
$xpath = $converter->toXPath($selector, 'child::');
|
||||
|
||||
return $this->filterRelativeXPath($xpath);
|
||||
}
|
||||
|
||||
$node = $this->getNode(0)->firstChild;
|
||||
|
||||
return $this->createSubCrawler($node ? $this->sibling($node) : array());
|
||||
@ -691,11 +703,7 @@ class Crawler implements \Countable, \IteratorAggregate
|
||||
*/
|
||||
public function filter($selector)
|
||||
{
|
||||
if (!class_exists(CssSelectorConverter::class)) {
|
||||
throw new \RuntimeException('To filter with a CSS selector, install the CssSelector component ("composer require symfony/css-selector"). Or use filterXpath instead.');
|
||||
}
|
||||
|
||||
$converter = new CssSelectorConverter($this->isHtml);
|
||||
$converter = $this->createCssSelectorConverter();
|
||||
|
||||
// The CssSelector already prefixes the selector with descendant-or-self::
|
||||
return $this->filterRelativeXPath($converter->toXPath($selector));
|
||||
@ -1148,4 +1156,16 @@ class Crawler implements \Countable, \IteratorAggregate
|
||||
|
||||
return $crawler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \RuntimeException If the CssSelector Component is not available
|
||||
*/
|
||||
private function createCssSelectorConverter(): CssSelectorConverter
|
||||
{
|
||||
if (!class_exists(CssSelectorConverter::class)) {
|
||||
throw new \RuntimeException('To filter with a CSS selector, install the CssSelector component ("composer require symfony/css-selector"). Or use filterXpath instead.');
|
||||
}
|
||||
|
||||
return new CssSelectorConverter($this->isHtml);
|
||||
}
|
||||
}
|
||||
|
@ -1004,6 +1004,37 @@ HTML;
|
||||
}
|
||||
}
|
||||
|
||||
public function testFilteredChildren()
|
||||
{
|
||||
$html = <<<'HTML'
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<body>
|
||||
<div id="foo">
|
||||
<div class="lorem">
|
||||
<p class="lorem"></p>
|
||||
</div>
|
||||
<div class="lorem">
|
||||
<span class="lorem"></span>
|
||||
</div>
|
||||
<span class="ipsum"></span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
|
||||
$crawler = new Crawler($html);
|
||||
$foo = $crawler->filter('#foo');
|
||||
|
||||
$this->assertEquals(3, $foo->children()->count());
|
||||
$this->assertEquals(2, $foo->children('.lorem')->count());
|
||||
$this->assertEquals(2, $foo->children('div')->count());
|
||||
$this->assertEquals(2, $foo->children('div.lorem')->count());
|
||||
$this->assertEquals(1, $foo->children('span')->count());
|
||||
$this->assertEquals(1, $foo->children('span.ipsum')->count());
|
||||
$this->assertEquals(1, $foo->children('.ipsum')->count());
|
||||
}
|
||||
|
||||
public function testParents()
|
||||
{
|
||||
$crawler = $this->createTestCrawler()->filterXPath('//li[1]');
|
||||
|
Reference in New Issue
Block a user