I am a top-performing Senior Software Engineer with extensive expertise in Laravel and PHP, covering the full software development lifecycle. My skills span full stack development, where I excel in leveraging Laravel's powerful features and PHP’s flexibility to build robust, scalable applications. I am adept at directing cross-functional teams to ensure precise and efficient software management and integration.

My experience includes collaborating with leadership teams to set and achieve strategic goals, innovating process improvements to enhance workflows, and maintaining strong internal controls. I am passionate about driving organizational growth through technical excellence and continuous optimization.

Securing Data in Laravel

Matt Goldsworthy

3 months ago

Introduction

When working with private information, it’s crucial to encrypt your data. Not only is it best practice, but in some cases, such as HIPAA regulations, it’s required by law. In this post, we’ll explore various methods to secure data using Laravel.

Identify Private Information

The first step is to identify private information, such as SSN or medical history. Laravel provides a built-in method called Crypt to secure this data. It uses the APP_KEY specified in your .env file. This key is very important and should be kept secret—consider using a key manager or other secure software to maintain it.

Encrypting the .env File

In some cases, you might need to encrypt your .env file. While deployment sites like Forge manage the .env file securely on the server, other environments may require you to encrypt it and decrypt it during deployment.

Creating a Model in Laravel

To demonstrate data encryption, let’s create a model using the following command:

php artisan make:model Patient -m

Migration File

In the migration file, define the schema for the model:


public function up()
{
    Schema::create('patients', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->text('history')->nullable();
        $table->string('ssn');
        $table->timestamps();
    });
}
            

Using Custom Getters and Setters

Within the model, you can use custom getters and setters for attributes like SSN and history. This method is useful if you use a key outside of the APP_KEY value.


attributes['_history'] = Crypt::encryptString($value);
    }

    public function getHistoryAttribute($value)
    {
        return Crypt::decryptString($value);
    }

    public function setSsnAttribute($value)
    {
        $this->attributes['ssn'] = Crypt::encryptString($value);
    }

    public function getSsnAttribute($value)
    {
        return Crypt::decryptString($value);
    }
}

Using Laravel’s Built-in Casts

Laravel’s built-in casting functionality simplifies encryption. This method uses the APP_KEY by default:


 'encrypted',
        'ssn' => 'encrypted',
    ];
}

This approach automatically handles encryption and decryption without needing custom getters and setters. For more information, check out this article from Laravel News.

Additional Security Practices

  • Key Rotation: Laravel supports key rotation using the APP_PREVIOUS_KEYS environment variable. This allows Laravel to use both current and previous keys for encryption and decryption.
  • Access Controls: Implement strong access controls using Laravel’s built-in functionality. For example, you can use gates and authorization:
  • 
    public function view(Model $model)
    {
        $this->authorize('view', $model);
        return view('model.show');
    }
                    
  • Logging Access: Log access to sensitive information to keep track of who accessed what and when.
  • Data Minimization: Collect only the data you need. Avoid storing unnecessary sensitive information.
  • Security Audits: Regularly review and update your security practices.
  • Data Encryption in Transit: Always use HTTPS to securely transmit data.

Conclusion

Securing data in Laravel involves using strong encryption, managing keys carefully, enforcing robust access controls, logging access, minimizing data collection, and regularly auditing your security practices. By following these guidelines, you can help ensure that your application’s sensitive data remains protected.

Optimizing Query Performance with Dynamic Date Ranges in Laravel and Livewire

Matt Goldsworthy

4 months ago

In web development, handling dynamic date ranges and optimizing query performance are essential for delivering fast and responsive applications. If you're working with Laravel, Livewire, and Eloquent, you have powerful tools at your disposal to manage these aspects effectively. In this post, we'll explore how to handle dynamic month ranges in SQL queries and discuss best practices for optimizing query performance.


Handling Dynamic Date Ranges in SQL Queries

When dealing with data that spans over various months or years, crafting SQL queries that can handle dynamic date ranges becomes crucial. Whether you're generating reports, displaying user activity, or analyzing data trends, you need to ensure your queries can adapt to different time periods.

Here's a basic example of how you can handle dynamic month ranges using Laravel's query builder:

use Illuminate\Support\Facades\DB;

$startDate = '2024-01-01'; // Example start date
$endDate = '2024-12-31';   // Example end date

$data = DB::table('your_table')
    ->whereBetween('date_column', [$startDate, $endDate])
    ->get();

In this example, whereBetween is used to filter records between the $startDate and $endDate. This method is straightforward but can be adapted to various use cases, such as fetching records for a specific month or year.


Leveraging Eloquent for Dynamic Queries

Eloquent, Laravel's ORM, simplifies working with database queries. If you're using Eloquent models, you can achieve similar results with a more intuitive syntax:

use App\Models\YourModel;

$data = YourModel::whereBetween('date_column', [$startDate, $endDate])
    ->get();

This approach not only keeps your code clean but also leverages Eloquent's built-in query-building capabilities.


Optimizing Query Performance

Performance optimization is a key aspect of working with large datasets or complex queries. Here are some tips to enhance query performance in Laravel and Livewire applications:


  • Indexing: Ensure that columns used in WHERE, JOIN, and ORDER BY clauses are indexed. Indexes significantly speed up query execution.

  • Caching: Use caching mechanisms to reduce database load. Laravel's built-in caching features, like query caching and result caching, can help:

  • $data = Cache::remember('your_cache_key', $seconds, function () use ($startDate, $endDate) {
        return YourModel::whereBetween('date_column', [$startDate, $endDate])
            ->get();
    });

  • Eager Loading: Reduce the number of queries by using Eloquent's eager loading to fetch related models in a single query:

  • $data = YourModel::with('relatedModel')
        ->whereBetween('date_column', [$startDate, $endDate])
        ->get();

  • Pagination: For large datasets, consider using pagination to load data in chunks rather than all at once:
  • $data = YourModel::whereBetween('date_column', [$startDate, $endDate])
        ->paginate(15);

Conclusion

Handling dynamic date ranges and optimizing query performance are critical for building efficient and scalable applications. With Laravel and Livewire, you have robust tools to manage these tasks effectively. By implementing best practices like indexing, caching, eager loading, and pagination, you can ensure your application performs well even under heavy loads.


Feel free to explore these techniques and adapt them to your specific needs. Happy coding!

Optimizing Widget Performance in Filament: A Real-World Example

Matt Goldsworthy

4 months ago

This week, we encountered significant performance issues with a Filament dashboard page that used multiple widgets. The memory consumption was high for each widget, and load times plummeted from milliseconds to several seconds. Intrigued by this problem, I decided to investigate further using a specific widget as a case study.

The Problem

Here’s a snapshot of the widget in question:


Widget Screenshot

The Debugbar output for this widget was revealing:


Debugbar Output

With 5000 requests and 4148 errors (seeded), the performance was notably poor. Given that the query in question was relatively simple, it was clear that something needed to change.

Analyzing The Code

Here’s a look at the code responsible for the inefficiency:


Code Screenshot

The code employed a while loop to query data for each month, returning models for each iteration. This approach doesn’t scale well; with larger datasets, such as 20,000 to 30,000 errors or requests, this method could become increasingly problematic.

Common Pitfalls with Eloquent

This situation highlights a common pitfall among developers, particularly those newer to the field. While Eloquent ORM is powerful and convenient, it’s easy to over-rely on it without considering performance implications. Eloquent might retrieve more data than necessary if queries are not carefully crafted.


I’ve encountered this myself and have to remind myself to use raw database queries when Eloquent becomes too cumbersome for complex scenarios.


A Better Approach

To address the issue, I decided to focus on the essential data: the count of errors per month. Given that requests are either successful or error-prone (with errors embedded in a nested result in an XML API), we only need to count errors by month. Here’s a more efficient SQL query to get the counts:



SELECT
    m.month,
    COALESCE(lrr.request_count, 0) AS request_count
FROM
    (SELECT 2 as month UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8) m
LEFT JOIN (
    SELECT
        MONTH(request_at) AS month,
        COUNT(*) AS request_count
    FROM
        logged_request_responses
    WHERE
        response_status != 200
        AND request_at BETWEEN "2024-02-04 00:00:00" AND "2024-08-04 23:59:59"
    GROUP BY
        MONTH(request_at)
) lrr
ON m.month = lrr.month
ORDER BY
    m.month
            

This query efficiently aggregates the error counts by month and should scale well even with larger datasets.

The Results

Although this solution was a quick fix, the performance improvement was remarkable:


Performance Improvement

The query now executes in 29ms compared to the previous approach, which involved 6 to 7 separate queries (one for each month).

Conclusion

While this optimization significantly improved performance, there’s always room for further refinement. This experience underscores the importance of balancing ORM convenience with raw query efficiency to handle large data sets effectively.