Skip to content

Commit b0621d4

Browse files
authored
Added missing CLI dependency (#1784)
* Added missing CLI dependency - added examples documentation - added monorepo validation to static analysis composer command - added more comlumns to FakeRandomOrdersExtractor * Updated dependencies
1 parent 602c7c3 commit b0621d4

12 files changed

Lines changed: 314 additions & 31 deletions

File tree

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@
402402
],
403403
"test:monorepo": "tools/monorepo/vendor/bin/monorepo-builder validate",
404404
"static:analyze": [
405+
"./tools/monorepo/vendor/bin/monorepo-builder validate",
405406
"@static:analyze:cs-fixer",
406407
"@static:analyze:phpstan",
407408
"@static:analyze:rector"

composer.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

documentation/examples.md

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
# Examples
2+
3+
This document provides comprehensive guidance for working with Flow PHP examples, including how to add new examples,
4+
update existing ones, and run them effectively.
5+
6+
## Overview
7+
8+
Flow PHP includes a comprehensive collection of executable examples organized by topic, demonstrating all major
9+
framework features. Each example is self-contained with isolated dependencies and serves as both documentation and
10+
integration testing.
11+
12+
## Directory Structure
13+
14+
Examples are organized hierarchically under `/examples/topics/` with the following structure:
15+
16+
```
17+
examples/
18+
├── run.php # Main execution script
19+
├── clean.php # Cleanup utility
20+
└── topics/ # Topic-based organization
21+
├── aggregations/ # Data aggregation operations (8 examples)
22+
├── data_frame/ # DataFrame operations (7 examples)
23+
├── data_reading/ # Data input methods (13 examples)
24+
├── data_writing/ # Data output methods (9 examples)
25+
├── filesystem/ # Filesystem operations (4 examples)
26+
├── join/ # Data joining (2 examples)
27+
├── partitioning/ # Data partitioning (4 examples)
28+
├── schema/ # Schema management (4 examples)
29+
├── transformations/ # Data transformations (12 examples)
30+
├── types/ # Type system (2 examples)
31+
└── window_functions/ # Window functions (1 example)
32+
```
33+
34+
### Standard Example Structure
35+
36+
Each example follows this consistent structure:
37+
38+
```
39+
examples/topics/{topic}/{example}/
40+
├── code.php # Main executable script (required)
41+
├── composer.json # Isolated dependencies (required)
42+
├── composer.lock # Locked dependencies (generated)
43+
├── output.txt # Expected output (required)
44+
├── description.md # Human-readable explanation (optional)
45+
├── priority.txt # Numeric priority for ordering (optional)
46+
├── hidden.txt # Hide from website (optional)
47+
├── input/ # Sample input data (optional)
48+
├── output/ # Generated output files (optional)
49+
├── vendor/ # Composer dependencies (generated)
50+
└── flow_php_example.zip # Distribution archive (generated)
51+
```
52+
53+
## Running Examples
54+
55+
### Command Line Interface
56+
57+
Use the main execution script to run examples:
58+
59+
```bash
60+
# Run all examples
61+
./examples/run.php
62+
63+
# Run specific topic
64+
./examples/run.php --topic=data_reading
65+
66+
# Run specific example
67+
./examples/run.php --topic=data_reading --example=csv
68+
69+
# Update dependencies instead of install
70+
./examples/run.php --composer-update
71+
72+
# Generate distribution archives
73+
./examples/run.php --composer-archive
74+
```
75+
76+
### Available Options
77+
78+
- `--topic (-t)`: Run examples from a specific topic
79+
- `--example (-e)`: Run a specific example (requires --topic)
80+
- `--composer-update (-u)`: Update dependencies instead of install
81+
- `--composer-archive (-a)`: Generate ZIP archives for distribution
82+
83+
### Integration with Build System
84+
85+
Examples are integrated into the build process:
86+
87+
```bash
88+
# Run examples as part of test suite
89+
composer test:examples
90+
91+
# Run full test suite including examples
92+
composer test
93+
```
94+
95+
Examples run automatically in CI/CD on changes to `examples/**` directory.
96+
97+
## Adding New Examples
98+
99+
### Step-by-Step Process
100+
101+
1. **Choose Topic and Name**
102+
- Use existing topics when possible
103+
- Create new topics only for distinct feature areas
104+
- Use descriptive, lowercase names with underscores
105+
106+
2. **Create Directory Structure**
107+
```bash
108+
mkdir -p examples/topics/{topic}/{example}
109+
cd examples/topics/{topic}/{example}
110+
```
111+
112+
3. **Create Required Files**
113+
114+
**`code.php`** - Main executable script:
115+
```php
116+
<?php
117+
declare(strict_types=1);
118+
119+
use function Flow\ETL\DSL\{data_frame, /* other functions */};
120+
use function Flow\ETL\Adapter\[Adapter]\{/* adapter functions */};
121+
122+
require __DIR__ . '/vendor/autoload.php';
123+
124+
data_frame()
125+
->read(/* extractor */)
126+
->transform(/* transformations */)
127+
->write(/* loader */)
128+
->run();
129+
```
130+
131+
**`composer.json`** - Dependencies:
132+
```json
133+
{
134+
"name": "flow-php/examples",
135+
"description": "Flow PHP - Examples",
136+
"license": "MIT",
137+
"type": "library",
138+
"require": {
139+
"flow-php/etl": "1.x-dev",
140+
"flow-php/etl-adapter-[specific]": "1.x-dev"
141+
},
142+
"archive": {
143+
"exclude": [".env", "vendor"]
144+
}
145+
}
146+
```
147+
148+
**`priority.txt`** - Numeric priority (1-99, lower = higher priority):
149+
```
150+
10
151+
```
152+
153+
4. **Add Optional Files**
154+
155+
**`description.md`** - Human-readable explanation:
156+
```markdown
157+
# Example Title
158+
159+
Brief description of what this example demonstrates.
160+
161+
## Key Features
162+
163+
- Feature 1
164+
- Feature 2
165+
166+
## Use Cases
167+
168+
When to use this pattern...
169+
```
170+
171+
**`input/`** directory - Sample data files if needed
172+
**`hidden.txt`** - Empty file to hide from website
173+
174+
5. **Test the Example**
175+
```bash
176+
# Test locally
177+
./examples/run.php --topic={topic} --example={example}
178+
```
179+
180+
## Updating Existing Examples
181+
182+
### Common Update Scenarios
183+
184+
1. **Code Changes**
185+
- Edit `code.php` following existing patterns
186+
- Test thoroughly with `./examples/run.php`
187+
- Update `output.txt` if output changes
188+
189+
2. **Dependency Updates**
190+
- Modify `composer.json` as needed
191+
- Run `./examples/run.php --composer-update --topic={topic} --example={example}` to refresh dependencies
192+
- Commit both `composer.json` and `composer.lock`
193+
194+
3. **Documentation Updates**
195+
- Edit `description.md` for clarity
196+
- Keep documentation concise and focused
197+
198+
4. **Priority Changes**
199+
- Modify `priority.txt` value (1-99)
200+
- Lower numbers appear first in listings
201+
202+
## Priority and Organization System
203+
204+
### Priority System
205+
206+
Examples use numeric priorities for ordering:
207+
208+
- **Range**: 1-99 (lower numbers = higher priority)
209+
- **Default**: 99 if `priority.txt` doesn't exist
210+
- **Application**: Both topics and individual examples
211+
- **Display**: Lower priority numbers appear first
212+
213+
### Visibility Control
214+
215+
- **`hidden.txt`**: Empty file that hides examples from website
216+
- **Use cases**: Internal examples, development utilities, deprecated examples
217+
- **Current usage**: Applied to some sequence generator examples
218+
219+
## Maintenance and Cleanup
220+
221+
### Cleanup Operations
222+
223+
Remove generated files and dependencies:
224+
225+
```bash
226+
# Clean all examples
227+
./examples/clean.php
228+
229+
# This removes:
230+
# - vendor/ directories
231+
# - flow_php_example.zip files
232+
```
233+
234+
### Archive Generation
235+
236+
Create distribution packages:
237+
238+
```bash
239+
# Generate archives for all examples
240+
./examples/run.php --composer-archive
241+
242+
# Archives are created as flow_php_example.zip in each example directory
243+
```
244+
245+
## Integration with Website
246+
247+
Examples are automatically integrated into the Flow PHP website:
248+
249+
- **Dynamic content**: Website reads examples directly from filesystem
250+
- **Priority ordering**: Uses `priority.txt` for display order
251+
- **Visibility**: Respects `hidden.txt` files
252+
- **Multi-format**: Supports various output formats (txt, xml, csv, json)
253+
- **Code highlighting**: Automatic syntax highlighting for code blocks

documentation/quick-start.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ It creates a new instance of the `Flow\ETL\Flow` class, which is the main class
4949

5050
[Data Frame Examples](/data_frame/#example)
5151

52+
[Examples Documentation](/documentation/examples)
53+
5254
## Extraction - Reading
5355

5456
The first step in creating a data processing pipeline is to read the data from a data source.

src/bridge/monolog/http/tests/Flow/Bridge/Monolog/Http/Tests/Unit/PSR7ProcessorSanitizationTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public function test_sanitizing_request_fields() : void
4444

4545
$record = $processor(['datetime' => new \DateTimeImmutable, 'channel' => 'http', 'level_name' => 'debug', 'message' => 'HTTP Request', 'context' => ['request' => $request]]);
4646

47+
/** @phpstan-ignore-next-line */
4748
$requestData = json_decode((string) $record['context']['request']['body'], true);
4849
\assert(\is_array($requestData));
4950

@@ -87,6 +88,7 @@ public function test_sanitizing_response_fields() : void
8788

8889
$record = $processor(['datetime' => new \DateTimeImmutable, 'channel' => 'http', 'level_name' => 'debug', 'message' => 'HTTP Response', 'context' => ['response' => $response]]);
8990

91+
/** @phpstan-ignore-next-line */
9092
$responseData = json_decode((string) $record['context']['response']['body'], true);
9193
\assert(\is_array($responseData));
9294

src/cli/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"require": {
1111
"php": "~8.2.0 || ~8.3.0 || ~8.4.0",
1212
"symfony/console": "^5.4 || ^6.4 || ^7.0",
13+
"symfony/uid": "^6.3 || ^7.0",
1314
"flow-php/etl": "self.version",
1415
"flow-php/etl-adapter-csv": "self.version",
1516
"flow-php/etl-adapter-parquet": "self.version",

src/core/etl/src/Flow/ETL/Row/Entry/XMLElementEntry.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public function definition() : Definition
104104

105105
public function duplicate() : self
106106
{
107-
return new self($this->name, $this->value ? $this->value->cloneNode(true) : null, $this->metadata);
107+
return new self($this->name, type_optional(type_instance_of(\DOMElement::class))->assert($this->value ? $this->value->cloneNode(true) : null), $this->metadata);
108108
}
109109

110110
public function is(Reference|string $name) : bool

src/core/etl/tests/Flow/ETL/Tests/Double/FakeRandomOrdersExtractor.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ public static function schema() : Schema
1919
{
2020
return schema(
2121
uuid_schema('order_id'),
22+
uuid_schema('seller_id'),
2223
datetime_schema('created_at'),
2324
datetime_schema('updated_at', true),
25+
datetime_schema('cancelled_at', true),
2426
float_schema('discount', true),
2527
string_schema('email'),
2628
string_schema('customer'),
@@ -56,12 +58,32 @@ public function extract(FlowContext $context) : \Generator
5658
['sku' => 'SKU_0005', 'name' => 'Product 5', 'price' => $faker->randomFloat(2, 0, 500)],
5759
];
5860

61+
$sellers = [
62+
$faker->uuid,
63+
$faker->uuid,
64+
$faker->uuid,
65+
$faker->uuid,
66+
$faker->uuid,
67+
];
68+
5969
for ($i = 0; $i < $this->count; $i++) {
70+
71+
$createdAt = \DateTimeImmutable::createFromMutable($faker->dateTimeThisYear);
72+
$cancelledAt = \random_int(1, 10) === 1 ? $createdAt->modify('+' . $faker->numberBetween(1, 5) . ' hours') : null;
73+
74+
if ($cancelledAt) {
75+
$updatedAt = $cancelledAt;
76+
} else {
77+
$updatedAt = \random_int(1, 3) === 1 ? $createdAt->modify('+' . $faker->numberBetween(1, 3) . ' days') : null;
78+
}
79+
6080
yield array_to_rows(
6181
[
6282
'order_id' => $faker->uuid,
63-
'created_at' => $faker->dateTimeThisYear,
64-
'updated_at' => \random_int(0, 1) === 1 ? $faker->dateTimeThisMonth : null,
83+
'seller_id' => $sellers[\random_int(0, \count($sellers) - 1)],
84+
'created_at' => $createdAt,
85+
'updated_at' => $updatedAt,
86+
'cancelled_at' => $cancelledAt,
6587
'discount' => \random_int(0, 1) === 1 ? $faker->randomFloat(2, 0, 50) : null,
6688
'email' => $faker->email,
6789
'customer' => $faker->firstName . ' ' . $faker->lastName,

0 commit comments

Comments
 (0)