I'm a code folder
I know it looks strange the first time you see it, but hear me out for a minute: I am a code folder.
class TwitterSyncCommand extends Command
{
protected $signature = 'twitter:sync {--clean}';
/** @var \Illuminate\Database\Eloquent\Collection<Mute> */
private Collection $mutes;
public function handle(Twitter $twitter) { /* … */ }
public function syncFromList(Twitter $twitter): void { /* … */ }
public function syncFromSearch(Twitter $twitter): void { /* … */ }
private function storeTweets(array $tweets, TweetFeedType $feedType): void { /* … */ }
private function shouldBeRejected(Tweet $tweet): ?RejectionReason { /* … */ }
}
I hide most of my code, most of the time. I have keyboard shortcuts to easily show and hide blocks of code; and when I open a file, all method and function bodies are collapsed by default.
The reason? I’m not a superhuman speed reader that understands dozens of lines of code at one glance. And… I also don’t have a two-metre-high screen.
I just can’t read and understand all of this — you know?
class TwitterSyncCommand extends Command
{
protected $signature = 'twitter:sync {--clean}';
/** @var \Illuminate\Database\Eloquent\Collection<Mute> */
private Collection $mutes;
public function handle(Twitter $twitter)
{
$this->mutes = Mute::query()->select('text')->get();
if ($this->option('clean')) {
$this->error('Truncating tweets!');
Tweet::truncate();
}
$this->syncFromSearch($twitter);
$this->syncFromList($twitter);
$this->info('Done');
}
public function syncFromList(Twitter $twitter): void
{
do {
$lastTweet = Tweet::query()
->where('feed_type', TweetFeedType::LIST)
->orderByDesc('tweet_id')
->first();
$tweets = $twitter->request('lists/statuses.json', 'GET', [
'list_id' => config('services.twitter.list_id'),
'since_id' => $lastTweet?->tweet_id,
'count' => 200,
'tweet_mode' => 'extended',
]);
$count = count($tweets);
if ($count === 0) {
$this->comment('No more new tweets');
} else {
$this->comment("Syncing {$count} tweets from list");
$this->storeTweets($tweets, TweetFeedType::LIST);
}
} while ($tweets !== []);
}
public function syncFromSearch(Twitter $twitter): void
{
do {
$lastTweet = Tweet::query()
->where('feed_type', TweetFeedType::SEARCH)
->orderByDesc('tweet_id')
->first();
$tweets = $twitter->request('/search/tweets.json', 'GET', [
'q' => 'phpstorm',
'since_id' => $lastTweet?->tweet_id,
'count' => 200,
'tweet_mode' => 'extended',
])->statuses;
$count = count($tweets);
if ($count === 0) {
$this->comment('No more new tweets');
} else {
$this->comment("Syncing {$count} tweets from search");
$this->storeTweets($tweets, TweetFeedType::SEARCH);
}
} while ($tweets !== []);
}
private function storeTweets(array $tweets, TweetFeedType $feedType): void
{
foreach ($tweets as $tweet) {
$subject = $tweet->retweeted_status ?? $tweet;
$tweet = Tweet::updateOrCreate([
'tweet_id' => $tweet->id,
], [
'state' => TweetState::PENDING,
'feed_type' => $feedType,
'text' => $subject->full_text ,
'user_name' => $subject->user->screen_name,
'retweeted_by_user_name' => isset($tweet->retweeted_status)
/** @phpstan-ignore-next-line */
? $tweet->user->screen_name
: null,
'created_at' => Carbon::make($subject->created_at),
'payload' => json_encode($tweet),
]);
if ($reason = $this->shouldBeRejected($tweet)) {
$tweet->update([
'state' => TweetState::REJECTED,
'rejection_reason' => $reason->message,
]);
}
(new ParseTweetText)($tweet);
}
}
private function shouldBeRejected(Tweet $tweet): ?RejectionReason
{
if ($tweet->isRetweet() && $tweet->feed_type === TweetFeedType::SEARCH) {
return RejectionReason::retweetedFromSearch();
}
// Reject tweets containing a specific word
foreach ($this->mutes as $mute) {
if ($tweet->containsPhrase($mute->text)) {
return RejectionReason::mute($mute->text);
}
}
// Reject replies
if ($tweet->getPayload()->in_reply_to_status_id) {
return RejectionReason::isReply();
}
// Reject mentions
if (str_starts_with($tweet->text, '@')) {
return RejectionReason::isMention();
}
// Reject non-english tweets
$language = $tweet->getPayload()->lang;
if ($language !== 'en') {
return RejectionReason::otherLanguage($language);
}
return null;
}
}
I feel overwhelmed looking at all that code, especially when I’m searching for one specific method.
Now, when I create a new file, it’s all fine, it’s a blank slate, and I’ve got control. But code grows rapidly, and real life projects more often than not include work in existing files instead of blank slates. So I really need a way to reduce cognitive load, I can’t “take it all in at once”. And that’s why I’ve grown to like code folding so much.
Give yourself a week to get used to it. Configure your IDE to automatically fold method bodies and functions, and assign proper key bindings to show and hide blocks. I promise you, you’ll like it.