Skip to content

Commit 02fb844

Browse files
committed
Workspace Node Update Command and fixes
- This command updates multiple nodes in a workspace based on a given SQL query
1 parent 9360d81 commit 02fb844

File tree

12 files changed

+382
-61
lines changed

12 files changed

+382
-61
lines changed

src/PHPCR/Util/Console/Command/NodeDumpCommand.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ protected function configure()
6565
->addOption('ref-format', 'uuid', InputOption::VALUE_REQUIRED, 'Set the way references should be displayed when dumping reference properties - either "uuid" (default) or "path"')
6666
->addArgument('identifier', InputArgument::OPTIONAL, 'Root path to dump', '/')
6767
->setDescription('Dump subtrees of the content repository')
68-
->setHelp(<<<EOF
68+
->setHelp(<<<HERE
6969
The <info>dump</info> command recursively outputs the name of the node specified
7070
by the <info>identifier</info> argument and its subnodes in a yaml-like style.
7171
@@ -75,7 +75,7 @@ protected function configure()
7575
By default the command filters out system nodes and properties (i.e. nodes and
7676
properties with names starting with 'jcr:'), the <info>sys_nodes</info> option
7777
allows to turn this filter off.
78-
EOF
78+
HERE
7979
)
8080
;
8181
}

src/PHPCR/Util/Console/Command/NodeTouchCommand.php

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ protected function configure()
102102
*/
103103
protected function execute(InputInterface $input, OutputInterface $output)
104104
{
105-
/** @var $session SessionInterface */
106-
$session = $this->getHelper('phpcr')->getSession();
105+
$helper = $this->getHelper('phpcr');
106+
$session = $helper->getSession();
107107

108108
$path = $input->getArgument('path');
109109
$type = $input->getOption('type');
@@ -155,45 +155,13 @@ protected function execute(InputInterface $input, OutputInterface $output)
155155
$node = $parentNode->addNode($nodeName, $type);
156156
}
157157

158-
foreach ($setProp as $set) {
159-
$parts = explode('=', $set);
160-
$output->writeln(sprintf(
161-
'<comment> > Setting property </comment>%s<comment> to </comment>%s',
162-
$parts[0], $parts[1]
163-
));
164-
$node->setProperty($parts[0], $parts[1]);
165-
}
166-
167-
foreach ($removeProp as $unset) {
168-
$output->writeln(sprintf(
169-
'<comment> > Unsetting property </comment>%s',
170-
$unset
171-
));
172-
$node->setProperty($unset, null);
173-
}
174-
175-
foreach ($addMixins as $addMixin) {
176-
$node->addMixin($addMixin);
177-
}
178-
179-
foreach ($removeMixins as $removeMixin) {
180-
$node->removeMixin($removeMixin);
181-
}
182-
183-
if ($dump) {
184-
$output->writeln('<info>Node dump: </info>');
185-
/** @var $property PropertyInterface */
186-
foreach ($node->getProperties() as $property) {
187-
$value = $property->getValue();
188-
if (!is_string($value)) {
189-
$value = print_r($value, true);
190-
}
191-
$output->writeln(sprintf('<comment> - %s = </comment>%s',
192-
$property->getName(),
193-
$value
194-
));
195-
}
196-
}
158+
$helper->processNode($output, $node, array(
159+
'setProps' => $setProp,
160+
'removeProps' => $removeProp,
161+
'addMixins' => $addMixins,
162+
'removeMixins' => $removeMixins,
163+
'dump' => $dump,
164+
));
197165

198166
$session->save();
199167
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the PHPCR Utils
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache Software License 2.0
19+
* @link http://phpcr.github.com/
20+
*/
21+
22+
namespace PHPCR\Util\Console\Command;
23+
24+
use Symfony\Component\Console\Command\Command;
25+
use Symfony\Component\Console\Input\InputOption;
26+
use Symfony\Component\Console\Input\InputArgument;
27+
use Symfony\Component\Console\Input\InputInterface;
28+
use Symfony\Component\Console\Output\OutputInterface;
29+
use Symfony\Component\Console\Helper\DialogHelper;
30+
31+
/**
32+
* Command which can update the properties of nodes found
33+
* using the given JCR query.
34+
*
35+
* @author Daniel Leech <daniel@dantleech.com>
36+
*/
37+
class WorkspaceNodeUpdateCommand extends Command
38+
{
39+
/**
40+
* {@inheritDoc}
41+
*/
42+
protected function configure()
43+
{
44+
parent::configure();
45+
46+
$this->setName('phpcr:workspace:node:update')
47+
->addArgument(
48+
'query',
49+
InputArgument::REQUIRED,
50+
'A query statement to execute')
51+
->addOption('force', null,
52+
InputOption::VALUE_NONE,
53+
'Use to bypass the confirmation dialog'
54+
)
55+
->addOption(
56+
'language', 'l',
57+
InputOption::VALUE_OPTIONAL,
58+
'The query language (sql, jcr_sql2')
59+
60+
->addOption('set-prop', 'p',
61+
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
62+
'Set node property on nodes use foo=bar'
63+
)
64+
->addOption('remove-prop', 'r',
65+
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
66+
'Remove property from nodes'
67+
)
68+
->addOption('add-mixin', null,
69+
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
70+
'Add a mixin to the nodes'
71+
)
72+
->addOption('remove-mixin', null,
73+
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
74+
'Remove mixin from the nodes'
75+
)
76+
->setDescription('Command to manipulate the nodes in the workspace.')
77+
->setHelp(<<<HERE
78+
The <info>workspace:node:update</info> command updates properties of nodes found
79+
by the given JCR query.
80+
81+
php bin/phpcr workspace:node:update "SELECT FROM nt:unstructured" --set-prop=foo=bar
82+
83+
The options for manipulating nodes are the same as with the
84+
<info>node:touch</info> command and
85+
can be repeated to update multiple properties.
86+
HERE
87+
);
88+
}
89+
90+
/**
91+
* {@inheritDoc}
92+
*/
93+
protected function execute(InputInterface $input, OutputInterface $output)
94+
{
95+
$sql = $input->getArgument('query');
96+
$language = strtoupper($input->getOption('language'));
97+
$setProp = $input->getOption('set-prop');
98+
$removeProp = $input->getOption('remove-prop');
99+
$addMixins = $input->getOption('add-mixin');
100+
$removeMixins = $input->getOption('remove-mixin');
101+
$force = $input->getOption('force');
102+
103+
$helper = $this->getHelper('phpcr');
104+
$session = $helper->getSession();
105+
106+
$query = $helper->createQuery($language, $sql);
107+
108+
$start = microtime(true);
109+
$result = $query->execute();
110+
$elapsed = microtime(true) - $start;
111+
112+
if (!$force) {
113+
$dialog = new DialogHelper();
114+
$force = $dialog->askConfirmation($output, sprintf(
115+
'<question>About to update %d nodes, do you want to continue Y/N ?</question>',
116+
count($result)
117+
), false);
118+
}
119+
120+
foreach ($result as $i => $row) {
121+
$output->writeln(sprintf(
122+
"<info>Updating node</info> %s.",
123+
$row->getPath()
124+
));
125+
126+
$node = $row->getNode();
127+
128+
$helper->processNode($output, $node, array(
129+
'setProp' => $setProp,
130+
'removeProp' => $removeProp,
131+
'addMixins' => $addMixins,
132+
'removeMixins' => $removeMixins,
133+
));
134+
}
135+
136+
$output->writeln(sprintf('<info>%.2f seconds</info>', $elapsed));
137+
138+
return 0;
139+
}
140+
}

src/PHPCR/Util/Console/Command/WorkspaceQueryCommand.php

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,10 @@ protected function execute(InputInterface $input, OutputInterface $output)
6161
$limit = $input->getOption('limit');
6262
$offset = $input->getOption('offset');
6363

64-
$session = $this->getHelper('phpcr')->getSession();
65-
$qm = $session->getWorkspace()->getQueryManager();
64+
$helper = $this->getHelper('phpcr');
65+
$session = $helper->getSession();
6666

67-
if (!defined('\PHPCR\Query\QueryInterface::'.$language)) {
68-
throw new \RuntimeException(sprintf(
69-
"Query language '\\PHPCR\\Query\\QueryInterface::%s' not defined.",
70-
$language
71-
));
72-
}
73-
74-
$query = $qm->createQuery($sql, constant('\PHPCR\Query\QueryInterface::'.$language));
67+
$query = $helper->createQuery($language, $sql);
7568

7669
if ($limit) {
7770
$query->setLimit($limit);

src/PHPCR/Util/Console/Helper/PhpcrHelper.php

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
use Symfony\Component\Console\Helper\Helper;
2525
use PHPCR\SessionInterface;
26+
use Symfony\Component\Console\Output\OutputInterface;
2627

2728
/**
2829
* Helper class to make the session instance available to console commands
@@ -63,4 +64,98 @@ public function getName()
6364
{
6465
return 'phpcr';
6566
}
67+
68+
/**
69+
* Process - or update - a given node.
70+
* Provides common processing for both touch
71+
* and update commands.
72+
*/
73+
public function processNode(OutputInterface $output, $node, $options)
74+
{
75+
$options = array_merge(array(
76+
'setProp' => array(),
77+
'removeProp' => array(),
78+
'addMixins' => array(),
79+
'removeMixins' => array(),
80+
'dump' => false,
81+
), $options);
82+
83+
foreach ($options['setProp'] as $set) {
84+
$parts = explode('=', $set);
85+
$output->writeln(sprintf(
86+
'<comment> > Setting property </comment>%s<comment> to </comment>%s',
87+
$parts[0], $parts[1]
88+
));
89+
$node->setProperty($parts[0], $parts[1]);
90+
}
91+
92+
foreach ($options['removeProp'] as $unset) {
93+
$output->writeln(sprintf(
94+
'<comment> > Unsetting property </comment>%s',
95+
$unset
96+
));
97+
$node->setProperty($unset, null);
98+
}
99+
100+
foreach ($options['addMixins'] as $addMixin) {
101+
$output->writeln(
102+
'<comment> > Adding mixin </comment>%s<comment>',
103+
$addMixin
104+
);
105+
106+
$node->addMixin($addMixin);
107+
}
108+
109+
foreach ($options['removeMixins'] as $removeMixin) {
110+
$output->writeln(
111+
'<comment> > Removing mixin </comment>%s<comment>',
112+
$removeMixin
113+
);
114+
115+
$node->removeMixin($removeMixin);
116+
}
117+
118+
if ($options['dump']) {
119+
$output->writeln('<info>Node dump: </info>');
120+
/** @var $property PropertyInterface */
121+
foreach ($node->getProperties() as $property) {
122+
$value = $property->getValue();
123+
if (!is_string($value)) {
124+
$value = print_r($value, true);
125+
}
126+
$output->writeln(sprintf('<comment> - %s = </comment>%s',
127+
$property->getName(),
128+
$value
129+
));
130+
}
131+
}
132+
}
133+
134+
/**
135+
* Create a PHPCR query using the given language and
136+
* query string.
137+
*
138+
* @param string Language type - SQL, SQL2
139+
* @param string JCR Query
140+
*
141+
* @return PHPCR/QueryInterface
142+
*/
143+
public function createQuery($language, $sql)
144+
{
145+
$session = $this->getSession();
146+
$qm = $session->getWorkspace()->getQueryManager();
147+
$language = strtoupper($language);
148+
149+
$constantName = '\PHPCR\Query\QueryInterface::'.$language;
150+
if (!defined($constantName)) {
151+
throw new \RuntimeException(sprintf(
152+
"Query language '\\PHPCR\\Query\\QueryInterface::%s' not defined.",
153+
$language
154+
));
155+
}
156+
157+
$query = $qm->createQuery($sql, constant($constantName));
158+
159+
return $query;
160+
}
66161
}

tests/PHPCR/Tests/Util/Console/Command/Stubs/MockNode.php renamed to tests/PHPCR/Tests/Stubs/MockNode.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace PHPCR\Tests\Util\Console\Command\Stubs;
3+
namespace PHPCR\Tests\Stubs;
44

55
use PHPCR\NodeInterface;
66

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace PHPCR\Tests\Util\Console\Command\Stubs;
3+
namespace PHPCR\Tests\Stubs;
44

55
use PHPCR\NodeType\NodeTypeManagerInterface;
66

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace PHPCR\Tests\Stubs;
4+
5+
use PHPCR\Query\RowInterface;
6+
7+
abstract class MockRow implements \Iterator, RowInterface
8+
{
9+
}
10+

tests/PHPCR/Tests/Util/Console/Command/BaseCommandTest.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
use PHPCR\Util\Console\Helper\PhpcrConsoleDumperHelper;
1313
use PHPCR\Util\Console\Helper\PhpcrHelper;
1414

15-
require_once(__DIR__.'/Stubs/MockNode.php');
15+
require_once(__DIR__.'/../../../Stubs/MockNode.php');
16+
require_once(__DIR__.'/../../../Stubs/MockNodeTypeManager.php');
17+
require_once(__DIR__.'/../../../Stubs/MockRow.php');
18+
1619

1720
abstract class BaseCommandTest extends \PHPUnit_Framework_TestCase
1821
{
@@ -35,7 +38,8 @@ public function setUp()
3538
$this->workspace = $this->getMock('PHPCR\WorkspaceInterface');
3639
$this->repository = $this->getMock('PHPCR\RepositoryInterface');
3740

38-
$this->node1 = $this->getMock('PHPCR\Tests\Util\Console\Command\Stubs\MockNode');
41+
$this->row1 = $this->getMock('PHPCR\Tests\Stubs\MockRow');
42+
$this->node1 = $this->getMock('PHPCR\Tests\Stubs\MockNode');
3943

4044
$this->dumperHelper = $this->getMockBuilder(
4145
'PHPCR\Util\Console\Helper\PhpcrConsoleDumperHelper'

0 commit comments

Comments
 (0)