Skip to content
This repository was archived by the owner on Dec 30, 2024. It is now read-only.

Commit b25b702

Browse files
committed
Creation of the services TrelloApi and WebCrawler to separate responsibilities.
1 parent 3390167 commit b25b702

File tree

6 files changed

+211
-161
lines changed

6 files changed

+211
-161
lines changed

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,11 @@ we use.
2929

3030
Deciding between single quotes or double quotes:
3131
Like I used a lot of variable interpolation I prefer
32-
use double quotes
32+
use double quotes
33+
34+
Creation of services
35+
Pass them to the command like arguments to be easily
36+
Interchangeable.
37+
38+
HttpClient can have a wrapper?
39+
Yes, query string parameters

index.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Trello\CLI;
4+
5+
use Symfony\Component\Console\Application;
6+
use Symfony\Component\Dotenv\Dotenv;
7+
use Symfony\Component\HttpClient\HttpClient;
8+
use Trello\CLI\Command\CreateLinkCardCommand;
9+
use Trello\CLI\Service\TrelloApi;
10+
use Trello\CLI\Service\WebCrawler;
11+
12+
require_once __DIR__ . "/vendor/autoload.php";
13+
14+
$dotenv = new Dotenv();
15+
$dotenv->load(__DIR__ . "/.env");
16+
17+
$httpClient = HttpClient::create();
18+
19+
$trelloApi = new TrelloApi($httpClient, $_ENV["TRELLO_API_KEY"], $_ENV["TRELLO_API_TOKEN"]);
20+
$webCrawler = new WebCrawler($httpClient);
21+
22+
$application = new Application();
23+
$application->add(new CreateLinkCardCommand($trelloApi, $webCrawler));
24+
$application->run();

src/Command/CreateLinkCardCommand.php

Lines changed: 56 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -2,155 +2,113 @@
22

33
namespace Trello\CLI\Command;
44

5-
use Exception;
65
use Symfony\Component\Console\Command\Command;
76
use Symfony\Component\Console\Helper\ProgressBar;
87
use Symfony\Component\Console\Input\InputInterface;
98
use Symfony\Component\Console\Output\OutputInterface;
109
use Symfony\Component\Console\Question\ChoiceQuestion;
1110
use Symfony\Component\Console\Question\Question;
12-
use Symfony\Component\DomCrawler\Crawler;
13-
use Symfony\Component\HttpClient\HttpClient;
11+
use Trello\CLI\Service\TrelloApi;
12+
use Trello\CLI\Service\WebCrawler;
1413

1514
class CreateLinkCardCommand extends Command
1615
{
17-
protected static $defaultName = 'trello-cli:create-link-card';
16+
protected static $defaultName = "trello-cli:create-link-card";
17+
18+
/** @var TrelloApi */
19+
private TrelloApi $trelloApi;
20+
21+
/** @var WebCrawler */
22+
private WebCrawler $webCrawler;
23+
24+
public function __construct(TrelloApi $trelloApi, WebCrawler $webCrawler)
25+
{
26+
// best practices recommend to call the parent constructor first and
27+
// then set your own properties. That wouldn"t work in this case
28+
// because configure() needs the properties set in this constructor
29+
$this->trelloApi = $trelloApi;
30+
$this->webCrawler = $webCrawler;
31+
32+
parent::__construct();
33+
}
1834

1935
protected function configure()
2036
{
21-
$this->setDescription('Creates a new Trello card with a checklist')
22-
->setHelp('
37+
$this->setDescription("Creates a new Trello card with a checklist")
38+
->setHelp("
2339
This command allows create a new Trello card with a checklist filled
2440
with links extracted from a given web page.
25-
');
41+
");
2642
}
2743

2844
protected function execute(InputInterface $input, OutputInterface $output)
2945
{
30-
$helper = $this->getHelper('question');
31-
32-
$key = $_ENV['TRELLO_API_KEY'];
33-
$token = $_ENV['TRELLO_API_TOKEN'];
46+
$helper = $this->getHelper("question");
3447

35-
$httpClient = HttpClient::create();
36-
37-
# Select board
38-
39-
// Todo Select the active boards only
40-
// Active != Archived
41-
$response = $httpClient->request(
42-
'GET',
43-
"https://api.trello.com/1/members/me/boards?fields=name&key={$key}&token={$token}",
44-
);
45-
46-
$trelloBoards = array_column($response->toArray(), null, 'name');
48+
### Select board ###
49+
$trelloBoards = $this->trelloApi->getTrelloBoards();
4750

4851
$question = new ChoiceQuestion(
49-
'Please select the board where you want to add the new card',
52+
"Please select the board where you want to add the new card",
5053
array_keys($trelloBoards),
5154
);
52-
$question->setErrorMessage('Board %s is invalid.');
55+
$question->setErrorMessage("Board %s is invalid.");
5356

5457
$boardName = $helper->ask($input, $output, $question);
55-
$output->writeln('You have just selected: '.$boardName);
56-
57-
# Select List
58-
$boardId = $trelloBoards[$boardName]['id'];
59-
$response = $httpClient->request(
60-
'GET',
61-
"https://api.trello.com/1/boards/{$boardId}/lists?fields=name&key={$key}&token={$token}",
62-
);
58+
$output->writeln("You have just selected: ".$boardName);
59+
### End Select board ###
6360

64-
$boardLists = array_column($response->toArray(), null, 'name');
61+
### Select List ###
62+
$boardLists = $this->trelloApi->getBoardLists($trelloBoards[$boardName]["id"]);
6563

6664
$question = new ChoiceQuestion(
67-
'Please select the list where you want to add the new card',
65+
"Please select the list where you want to add the new card",
6866
array_keys($boardLists),
6967
);
70-
$question->setErrorMessage('List %s is invalid.');
68+
$question->setErrorMessage("List %s is invalid.");
7169

7270
$listName = $helper->ask($input, $output, $question);
73-
$output->writeln('You have just selected: '.$listName);
71+
$output->writeln("You have just selected: ".$listName);
72+
### End Select List ###
7473

75-
# Set the name of the new card
76-
$question = new Question('Please enter the name of the new card: ');
74+
### Set the name of the new card ###
75+
$question = new Question("Please enter the name of the new card: ");
7776
$newCardName = $helper->ask($input, $output, $question);
77+
### End Set the name of the new card ###
7878

79-
# Set the web page from where to extract the links
80-
$question = new Question('Please enter the url from where to extract the links: ');
79+
### Set the web page from where to extract the links ###
80+
$question = new Question("Please enter the url from where to extract the links: ");
8181
$url = $helper->ask($input, $output, $question);
82+
### End Set the web page from where to extract the links ###
8283

83-
# Narrow dow the section from where to extract the links
84-
$question = new Question('Please enter a selector to narrow down the link extraction: ', '');
84+
### Narrow dow the section from where to extract the links ###
85+
$question = new Question("Please enter a selector to narrow down the link extraction: ", "");
8586
$selector = $helper->ask($input, $output, $question);
87+
### Narrow dow the section from where to extract the links ###
8688

87-
# Extract the links
88-
$httpClient = HttpClient::create();
89-
90-
try {
91-
$response = $httpClient->request(
92-
'GET',
93-
$url
94-
);
95-
$content = $response->getContent();
96-
$crawler = new Crawler($content);
97-
$links = array_filter(
98-
$crawler->filter($selector . ' a')->each(
99-
fn(Crawler $aCrawler) => [
100-
'text' => $aCrawler->text(),
101-
'href' => $aCrawler->attr('href')
102-
]
103-
),
104-
fn(array $link) => !empty($link['href'])
105-
);
106-
} catch (Exception $exception) {
107-
echo 'error';
108-
}
109-
110-
# Create a new card
111-
$idList = $boardLists[$listName]['id'];
112-
113-
$response = $httpClient->request(
114-
"POST",
115-
"https://api.trello.com/1/cards?idList={$idList}&key={$key}&token={$token}",
116-
["body" => ["name" => $newCardName]]
117-
);
118-
119-
$newCard = $response->toArray();
89+
$links = $this->webCrawler->extractLinksFrom($url, $selector);
12090

121-
# Create a new Checklist
122-
$idCard = $newCard["id"];
91+
$newCard = $this->trelloApi->createCard($newCardName, $boardLists[$listName]["id"]);
92+
$newChecklist = $this->trelloApi->createChecklist($newCard["id"]);
12393

124-
$response = $httpClient->request(
125-
"POST",
126-
"https://api.trello.com/1/checklists?idCard={$idCard}&key={$key}&token={$token}",
127-
);
94+
$this->createCheckItems($links, $newChecklist["id"], $output);
12895

129-
$newChecklist = $response->toArray();
96+
$output->writeln("");
97+
$output->writeln("The card: {$newCardName} have been created");
13098

131-
# Create the checkitems
132-
$idChecklist = $newChecklist['id'];
99+
return 0;
100+
}
133101

134-
// creates a new progress bar
102+
private function createCheckItems(array $links, $idCheckList, OutputInterface $output)
103+
{
135104
$progressBar = new ProgressBar($output, count($links));
136105
$progressBar->start();
137106

138107
foreach ($links as $link) {
139-
$text = $link["text"];
140-
$href = $link["href"];
141-
$httpClient->request(
142-
"POST",
143-
"https://api.trello.com/1/checklists/{$idChecklist}/checkItems?key={$key}&token={$token}",
144-
["body" => ["name" => "[{$text}]({$href})"]]
145-
);
108+
$this->trelloApi->createCheckItems($idCheckList, $link['text'], $link['href']);
146109
$progressBar->advance();
147110
}
148111

149112
$progressBar->finish();
150-
151-
$output->writeln('');
152-
$output->writeln("The new card: {$newCardName} have been created");
153-
154-
return 0;
155113
}
156114
}

src/Service/TrelloApi.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace Trello\CLI\Service;
4+
5+
use Symfony\Contracts\HttpClient\HttpClientInterface;
6+
7+
class TrelloApi
8+
{
9+
private HttpClientInterface $httpClient;
10+
private array $defaultQuery;
11+
12+
public function __construct(HttpClientInterface $httpClient, string $key, string $token)
13+
{
14+
$this->httpClient = $httpClient;
15+
$this->defaultQuery = [
16+
"key" => $key,
17+
"token" => $token,
18+
];
19+
}
20+
21+
// Todo Select the active boards only (Active != Archived)
22+
public function getTrelloBoards(): array
23+
{
24+
$response = $this->httpClient->request(
25+
"GET",
26+
"https://api.trello.com/1/members/me/boards",
27+
["query" => array_merge($this->defaultQuery, ['fields' => 'name'])]
28+
);
29+
30+
return array_column($response->toArray(), null, "name");
31+
}
32+
33+
public function getBoardLists(string $boardId): array
34+
{
35+
$response = $this->httpClient->request(
36+
"GET",
37+
"https://api.trello.com/1/boards/{$boardId}/lists",
38+
["query" => array_merge($this->defaultQuery, ['fields' => 'name'])]
39+
);
40+
41+
return array_column($response->toArray(), null, "name");
42+
}
43+
44+
public function createCard(string $cardName, string $idList): array
45+
{
46+
$response = $this->httpClient->request(
47+
"POST",
48+
"https://api.trello.com/1/cards",
49+
[
50+
"body" => ["name" => $cardName],
51+
"query" => array_merge($this->defaultQuery, ["idList" => $idList])
52+
]
53+
);
54+
55+
return $response->toArray();
56+
}
57+
58+
public function createChecklist(string $idCard): array
59+
{
60+
$response = $this->httpClient->request(
61+
"POST",
62+
"https://api.trello.com/1/checklists",
63+
["query" => array_merge($this->defaultQuery, ['idCard' => $idCard])]
64+
);
65+
66+
return $response->toArray();
67+
}
68+
69+
public function createCheckItems(string $idChecklist, string $text, string $href): void
70+
{
71+
$this->httpClient->request(
72+
"POST",
73+
"https://api.trello.com/1/checklists/{$idChecklist}/checkItems",
74+
[
75+
"body" => ["name" => "[{$text}]({$href})"],
76+
"query" => array_merge($this->defaultQuery, ['fields' => 'name'])
77+
]
78+
);
79+
}
80+
}

src/Service/WebCrawler.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Trello\CLI\Service;
4+
5+
use Exception;
6+
use Symfony\Component\DomCrawler\Crawler;
7+
use Symfony\Contracts\HttpClient\HttpClientInterface;
8+
9+
class WebCrawler
10+
{
11+
private HttpClientInterface $httpClient;
12+
13+
public function __construct(HttpClientInterface $httpClient)
14+
{
15+
$this->httpClient = $httpClient;
16+
}
17+
18+
public function extractLinksFrom(string $url, string $selector): array
19+
{
20+
try {
21+
$response = $this->httpClient->request(
22+
"GET",
23+
$url
24+
);
25+
$content = $response->getContent();
26+
$crawler = new Crawler($content);
27+
28+
return array_filter(
29+
$crawler->filter($selector . " a")->each(
30+
fn(Crawler $aCrawler) => [
31+
"text" => $aCrawler->text(),
32+
"href" => $aCrawler->attr("href")
33+
]
34+
),
35+
fn(array $link) => !empty($link["href"])
36+
);
37+
} catch (Exception $exception) {
38+
echo "An error happened: ".$exception->getMessage();
39+
40+
return [];
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)