Skip to content

Instantly share code, notes, and snippets.

@jpalala
Forked from joewinehub/Git.md
Created June 29, 2025 01:24
Show Gist options
  • Save jpalala/94f73423b59266fcc9f40020ab1fbd25 to your computer and use it in GitHub Desktop.
Save jpalala/94f73423b59266fcc9f40020ab1fbd25 to your computer and use it in GitHub Desktop.
Git and Symfony things

Yes — if you're using Symfony Messenger, and you're adding a MessageHandler, you typically need to register it so Symfony knows which handler processes which message.

In most cases, if you're following naming conventions and using autowiring, you don’t need to manually configure anything in YAML — Symfony can detect it automatically.

But there are cases where you do need to configure it explicitly, especially if:

  • You're not using autowiring/autoconfiguration
  • You're customizing which transport (queue) handles which message
  • You're overriding defaults

✅ 1. Automatic Handler Discovery (Recommended)

If you're using autowiring + autoconfiguration (default in Symfony), this is all you need:

Message Class

// src/Message/SendReportMessage.php
namespace App\Message;

class SendReportMessage
{
    public function __construct(public int $reportId) {}
}

Handler

// src/MessageHandler/SendReportMessageHandler.php
namespace App\MessageHandler;

use App\Message\SendReportMessage;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;

final class SendReportMessageHandler implements MessageHandlerInterface
{
    public function __invoke(SendReportMessage $message)
    {
        // handle it
    }
}

If services.yaml has autoconfigure enabled (which it does by default):

# config/services.yaml
services:
    _defaults:
        autowire: true
        autoconfigure: true

You don’t need to do anything else. Symfony will discover the handler.


⚙️ 2. Manually Mapping Handlers in YAML

If you want to manually bind a handler (or customize the transport), you can do something like this:

config/services.yaml

services:
    App\MessageHandler\SendReportMessageHandler:
        tags: [messenger.message_handler]

Or to assign it to a specific bus:

services:
    App\MessageHandler\SendReportMessageHandler:
        tags:
            - { name: messenger.message_handler, bus: messenger.bus.default }

🚚 3. Assigning Messages to Transports (Queues)

In messenger.yaml, you define which messages go to which transport:

# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            async: '%env(MESSENGER_TRANSPORT_DSN)%'

        routing:
            'App\Message\SendReportMessage': async

This says: when you dispatch SendReportMessage, send it to the async transport (e.g., a queue like RabbitMQ, Redis, SQS, etc.).


✅ Summary

Task YAML Needed?
Just writing a handler ❌ No
Manually binding services ✅ Maybe
Assigning message to transport ✅ Yes

Want me to check if your config needs any YAML, or help register a specific handler/queue?

Undoing git stash

To undo a git stash, it depends on what exactly you mean by “undo”:


🔁 1. If you applied a stash and want to undo the changes:

git stash apply       # or git stash pop

This applies the stash to your working directory. If you did this and want to undo the changes it made:

git reset --hard      # Discards all uncommitted changes
git clean -fd         # Removes untracked files and directories (be careful!)

⚠️ Warning: This will remove all local changes not committed yet.


🔙 2. If you want to revert the stash application (but haven’t popped it):

Use git stash apply (instead of pop) so the stash stays in the list. Then you can undo changes using reset and clean as shown above.


💥 3. If you popped a stash and want it back:

If you used git stash pop and now regret it, you might recover it if you haven’t done too much else yet:

git fsck --lost-found

Then look for something like a stash commit (e.g., dangling commit 1234abcd) and recover it like:

git stash apply 1234abcd

📦 4. If you want to delete a stash (i.e., permanently undo it):

git stash drop stash@{0}
# or delete all stashes:
git stash clear

Let me know your specific case and I’ll tailor the command for that.

Symfony automatically fills in credentials (like database usernames, passwords, API keys, etc.) using a combination of environment variables, configuration files, and service injection. Here's a breakdown of how this works:


🔑 1. Environment Variables

Symfony uses environment variables (usually stored in .env, .env.local, or actual server environment) to store sensitive configuration values.

Example: .env

DATABASE_URL="mysql://user:[email protected]:3306/db_name"
MAILER_DSN="smtp://username:[email protected]"

Symfony reads these variables using the %env()% syntax in configuration files.


⚙️ 2. Configuration Files (e.g. config/packages/)

Services like Doctrine, Messenger, Mailer, etc. use these environment variables.

Example: config/packages/doctrine.yaml

doctrine:
    dbal:
        url: '%env(resolve:DATABASE_URL)%'

This tells Symfony to look for DATABASE_URL in the environment and use its value.


🧩 3. Service Injection

When you create services (e.g., your own classes or Symfony components), Symfony injects dependencies like Doctrine, mailer, HTTP clients, etc., with the credentials already configured.

Example: You inject MailerInterface and it's already configured using the MAILER_DSN you provided.

public function __construct(MailerInterface $mailer) {
    $this->mailer = $mailer; // Already configured with credentials from env
}

🛡️ 4. Secrets Vault (Optional)

For production, Symfony can manage credentials using encrypted secrets stored in config/secrets/. You can use:

php bin/console secrets:set MAILER_DSN

Then Symfony decrypts and injects these when needed.


✅ Summary

Symfony fills in credentials automatically by:

  1. Reading from .env or server environment.
  2. Wiring them into configuration files using %env()%.
  3. Injecting the configured services into your app.
  4. Optionally using encrypted secrets for production.

Let me know if you want to trace a specific credential, like database or an API key.

Great to hear you're diving into React! Since you want to get good at useRedux, useDispatch, and useContext, I’ll lay out a learning path specifically focused on these concepts. Here’s a step-by-step roadmap tailored for you:


Step 1: Master React Fundamentals First

Before jumping into Redux or Context, make sure you understand:

  • JSX and Components (Functional components with hooks)
  • Props and State
  • Lifecycle methods (via useEffect)
  • Conditional rendering
  • Event handling

📘 If you're using the provided document (which I’ll refer to as the guide), look at the “React Basics” and “Hooks Overview” sections.


Step 2: Learn useContext for State Sharing

Before Redux, understand how React handles shared state natively with Context API.

  1. Create a Context:

    const ThemeContext = React.createContext();
  2. Provide the Context at a high level:

    <ThemeContext.Provider value={{ darkMode: true }}>
      <App />
    </ThemeContext.Provider>
  3. Consume with useContext:

    const { darkMode } = useContext(ThemeContext);

🧠 Why? Context is lightweight and great for small/medium apps.

📌 In the guide, check the "Context vs Redux" section and the "State Management with Context API" part.


Step 3: Learn Redux Step-by-Step

Redux is powerful for managing complex global state. Focus on the modern Redux Toolkit + hooks (useSelector, useDispatch).

3.1 Install Redux Toolkit

npm install @reduxjs/toolkit react-redux

3.2 Create a Slice (feature logic)

import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: (state) => { state.value += 1; },
    decrement: (state) => { state.value -= 1; },
  },
});

export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;

3.3 Configure Store

import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';

export const store = configureStore({
  reducer: { counter: counterReducer },
});

3.4 Provide Store in your App

import { Provider } from 'react-redux';
import { store } from './store';

<Provider store={store}>
  <App />
</Provider>

3.5 Use useSelector and useDispatch

import { useSelector, useDispatch } from 'react-redux';
import { increment } from './counterSlice';

function Counter() {
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => dispatch(increment())}>+</button>
    </div>
  );
}

📌 Refer to the Redux Toolkit Usage and Hooks with Redux sections in your guide for detailed code and best practices.


Step 4: Compare and Decide When to Use What

  • Use useContext for lightweight global state (e.g., theme, language).
  • Use Redux (with useDispatch, useSelector) for large-scale apps with complex state logic, async actions, etc.

Look at the "When to use Context vs Redux" table in your guide — it's a practical comparison for real-world projects.


✅ Step 5: Build Small Projects (Critical!)

Start with small apps like:

  • Counter App (with Redux)
  • Theme Toggle (with useContext)
  • Todo List (with Redux Toolkit)

These will reinforce your understanding of useDispatch, useSelector, and useContext.


✅ Bonus Tips:

  • Stick to function components + hooks.
  • Use Redux DevTools for debugging.
  • Write typed Redux code with TypeScript (once you’re comfortable).

If you’d like, I can give you a starter repo structure or tailored project ideas for practice. Let me know!

Here's a point-by-point comparison to Laravel for the details you provided about Symfony's scheduled tasks:

  1. Symfony Commands Overview

    • Symfony: Symfony commands are PHP classes that extend Command and are executed from the CLI.
    • Laravel: Laravel's commands are similar, extending the Command class and executed with php artisan. Both Symfony and Laravel allow for defining command logic with handle() (Laravel) or execute() (Symfony).
  2. Scheduling Jobs in Symfony

    • Symfony: Symfony doesn't have a built-in task scheduler, so you rely on external tools like cron jobs or Windows Task Scheduler.
    • Laravel: Laravel has a built-in task scheduler (php artisan schedule:run) that allows you to define and schedule tasks directly within the framework.
  3. Setting up Cron Jobs (Common Method)

    • Symfony: You create a cron job to call Symfony commands at intervals (e.g., 0 * * * * /usr/bin/php /path/to/your/project/bin/console your:command).
    • Laravel: Laravel requires just one cron job to call the task scheduler (php artisan schedule:run) and then tasks are scheduled within the framework itself.
  4. Symfony and Cron

    • Symfony: You define the frequency of execution using cron or Task Scheduler. You also have to handle logging manually through Symfony's logging tools.
    • Laravel: Laravel allows you to define the schedule in the framework using its fluent scheduler interface (e.g., ->daily(), ->hourly()) and automatically handles logs and monitoring for scheduled tasks.
  5. Task Scheduling in Symfony with Bundles

    • Symfony: You can use bundles like CronBundle or SchedulerBundle to configure tasks directly inside Symfony, but still rely on an external tool like cron for scheduling.
    • Laravel: Task scheduling is fully integrated within Laravel, so there's no need for an external tool; you define the schedule directly within the framework using ->daily() or other frequency methods.
  6. Overview of Workflow

    • Symfony: You create a Symfony command, configure an external scheduler like cron, and monitor tasks via logs.
    • Laravel: You define the schedule within Laravel's built-in scheduler, and cron only needs to invoke php artisan schedule:run at a set interval.

Summary Comparison:

  • Symfony: Uses external tools like cron or Windows Task Scheduler to schedule tasks after creating commands. No built-in scheduler.
  • Laravel: Has a built-in task scheduler (php artisan schedule:run), making task scheduling more integrated and seamless compared to Symfony.

Let me know if you'd like any more specific details!

To code in Symfony in a decoupled way, you're aiming to separate concerns—so that your business logic, presentation, and infrastructure (like DB, APIs, etc.) are modular and independent. This makes your app more testable, maintainable, and adaptable to change (e.g. switching from REST to GraphQL, or from MySQL to Redis).

Here’s a practical breakdown to achieve that:


🧱 1. Use a Clean Architecture / Hexagonal (Ports & Adapters)

This is the heart of decoupling.

Layers:

[ Controller (Symfony HTTP layer) ]
          ↓
[ Application Layer / Services ]
          ↓
[ Domain Layer (Entities, Value Objects, Interfaces) ]
          ↓
[ Infrastructure (Doctrine, APIs, File System, etc.) ]

Each layer depends only on the layer below it, not sideways or upwards.


🗂️ 2. Separate Domain from Infrastructure

✅ Domain:

  • Pure PHP classes.
  • No Symfony annotations.
  • No Doctrine dependencies.
  • No HTTP awareness.
  • Only business rules.
class Reservation
{
    private Customer $customer;
    private \DateTimeImmutable $date;

    public function __construct(Customer $customer, \DateTimeImmutable $date) {
        $this->customer = $customer;
        $this->date = $date;
    }

    public function reschedule(\DateTimeImmutable $newDate): void {
        $this->date = $newDate;
    }
}

❌ Bad (Tightly Coupled):

#[ORM\Entity]
class Reservation {
  // tightly coupled to Doctrine!
}

🧩 3. Use Interfaces to Decouple Infrastructure

Define interfaces in the domain or application layer:

interface ReservationRepositoryInterface
{
    public function save(Reservation $reservation): void;
    public function findByCustomer(Customer $customer): ?Reservation;
}

Then implement it in infrastructure:

class DoctrineReservationRepository implements ReservationRepositoryInterface
{
    public function save(Reservation $reservation): void {
        // use Doctrine to persist
    }

    public function findByCustomer(Customer $customer): ?Reservation {
        // query using Doctrine
    }
}

Register it in services.yaml:

App\Domain\ReservationRepositoryInterface: '@App\Infrastructure\DoctrineReservationRepository'

🧠 4. Application Services (Use Cases)

Write services that coordinate behavior.

class NominateCustomerService
{
    public function __construct(
        private ReservationRepositoryInterface $repository,
        private NotificationService $notifier,
    ) {}

    public function execute(NominateCustomerCommand $command): void {
        $reservation = new Reservation($command->customer, $command->date);
        $this->repository->save($reservation);
        $this->notifier->notify($command->customer, 'Nominated successfully');
    }
}

🧪 5. Test with In-Memory Implementations

When the domain is decoupled from Symfony or Doctrine, you can easily write tests:

$repository = new InMemoryReservationRepository();
$notifier = new FakeNotifier();
$service = new NominateCustomerService($repository, $notifier);

$service->execute(new NominateCustomerCommand($customer, $date));

🌍 6. Use Symfony Only as an Adapter

The controller should be a thin adapter:

#[Route('/nominate', methods: ['POST'])]
public function nominate(Request $request): Response
{
    $command = new NominateCustomerCommand(
        new Customer($request->get('email')),
        new \DateTimeImmutable($request->get('date'))
    );

    $this->nominateCustomerService->execute($command);

    return new JsonResponse(['status' => 'ok']);
}

🔄 Optional: Messenger for Decoupled Async

If you want to make your application fully decoupled from sync HTTP:

  • Dispatch commands via Symfony Messenger.
  • Handle them in separate handlers.
  • Benefit from background processing and retrying logic.

TL;DR — Symfony in a Decoupled Way:

Layer Responsibility Should Know About
Domain Business logic Nothing external
Application Use cases Domain, Interfaces
Infrastructure I/O (DB, APIs) Symfony, Doctrine
Controller HTTP interface Application services

Let me know what kind of app you're building (e.g. reservation system, e-commerce, API backend), and I can give you a real example structure.

Symfony Cheat Sheet

Cache Clearing

To clear the Symfony cache, use one of the following commands:

  • Clear the entire cache:
    php bin/console cache:clear
  • Clear all cache pools:
    php bin/console cache:pool:clear cache.global_clearer
  • Clear a specific cache pool:
    php bin/console cache:pool:clear <pool_name>

Getting Routes

To list all registered routes along with their paths, controllers, and other details, run:

php bin/console debug:router

VarDump Server

Debugging with the Symfony VarDump server:

  1. Install the package (for development only):
    composer require --dev symfony/var-dumper
  2. Start the VarDump server:
    php bin/console server:dump
  3. More information: Symfony VarDumper Docs

Running the Messenger (Queue Background Worker)

Consume messages from a queue with:

php bin/console messenger:consume <queueName> -vv

For example, consuming the async queue:

php bin/console messenger:consume async -vv

Testing a Queue

To ensure that a queue is working properly:

  1. Send a test message to the queue:
    php bin/console messenger:dispatch 'App\Message\YourTestMessage'
  2. Consume the message manually:
    php bin/console messenger:consume async -vv
  3. Check the transport status:
    php bin/console messenger:stats
    This will display the number of messages in each transport.

If the message is processed correctly, then probably queue is working as expected.

QueryBuilder Things

Absolutely! Here's a solid list of tips for using Doctrine's QueryBuilder in Symfony efficiently and cleanly:


🚀 Basic Usage Tips

  1. Always alias your root entity
    Use something short like 'c' for Customer, 'u' for User, etc. It makes your DQL much cleaner:

    $qb = $this->createQueryBuilder('c');
  2. Use setParameters() for safety
    Always bind values with parameters to prevent SQL injection and benefit from Doctrine's type handling:

    $qb->where('c.status = :status')
       ->setParameter('status', 'active');
  3. Use leftJoin() when the relation might be missing
    Prevents breaking the query if the join isn't always there:

    $qb->leftJoin('c.orders', 'o');
  4. Avoid heavy logic in SELECT unless needed
    Things like CONCAT(...) or complex functions should only go in if you're actually returning that field. Keep it simple unless it's for a computed column.


🧠 Structural & Clean Code Tips

  1. Build your columns in an array map
    Here is an example using CONCAT in MySQL

    $columns = [
        'fullName' => "CONCAT(c.firstName, ' ', c.lastName)",
    ];
  2. Split logic into reusable Criteria
    Use Doctrine's Criteria or just conditionally chain to the builder:

    if ($isArchived) {
        $qb->andWhere('c.archived = :archived')
           ->setParameter('archived', true);
    }
  3. Use Expr() for advanced logic
    It gives you a fluent way to build conditions:

    $expr = $qb->expr();
    $qb->andWhere($expr->orX(
        $expr->isNull('c.deletedAt'),
        $expr->eq('c.active', true)
    ));

⚙️ Performance Tips

  1. Use select() early and only fetch what you need
    Especially for large data sets — always prefer:

    $qb->select('c.email', 'c.createdAt');

    over select('c') if you're not hydrating full entities.

  2. Paginate using setFirstResult() and setMaxResults()
    You're already doing this, and it’s the best way for paginated APIs.

  3. Avoid N+1 problems
    Use joins + select properly to eager load what you need:

$qb->leftJoin('c.orders', 'o')
   ->addSelect('o');

💡 Debugging Tips

  1. Use getQuery()->getSQL() to see raw SQL
    Great for debugging what’s actually sent to DB:
dd($qb->getQuery()->getSQL());
  1. Use Doctrine Profiler in Symfony Web Debug Toolbar
    It shows you all queries being executed per request, great to spot performance issues.

🧩 Integration Tips

  1. Integrate with custom BaseQueryBuilder for filtering/searching
    Like you're doing — if you build reusable methods like .buildFilters(), keep the logic in one place and pass only what you need.

  2. Don’t use ->getResult() unless you need entities
    If you're using SELECT partial or scalar columns, use:

->getArrayResult()
  1. Hydrate wisely
    Choose between getResult(), getArrayResult(), getOneOrNullResult() depending on the use case.

If you’re doing complex search/filter/export logic (like your email-only mode), make sure your query is always expressive but still performant. You're on the right track with the column map and filtering logic.


Generating fixtures

Got it — you want:

✅ A PHP script (not Node)
✅ That generates INSERT SQLs for your reservations table
✅ With experienceName, dateTime, netAmountPaidCents randomized a bit
✅ No UI/form, just generate the SQL lines (you’ll paste/run them yourself)
✅ Ideally you want it to fit Symfony standards if possible (fixtures)


Interesting PHP Pro tricks

🧪 Pro Tip: How to test what PHP "sees" when using Json_decode()?

You can debug with:

var_dump(json_decode($jsonString, true)); // associative
var_dump(json_decode($jsonString));       // objects

Or even this to visualize encoding:

$data = json_decode($jsonString, true);
echo json_encode($data, JSON_PRETTY_PRINT);

TL;DR:

PHP doesn't "know" these were objects originally. It just infers from:

  • Numeric arrays ➡️ encode as JSON arrays
  • Associative arrays ➡️ encode as JSON objects

DDD things

Controller design and return type

Your method's return type should stay JsonResponse, because even though the method can throw exceptions internally, you catch those exceptions inside the method and convert them into JSON error responses.

In PHP and Symfony controller methods:

If your method handles exceptions internally and always returns a response, then the return type should be the actual return type (JsonResponse here).

If your method would let exceptions bubble up (not catch them) and not return a value, you could consider : never or no explicit return type, but that’s rare in controllers.

Since you catch exceptions inside and return a JSON error, your method always returns a JsonResponse.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment