Categories
Resources

Links and resources about Symfony Doctrine fixtures

Symfony cast page about fixtures.

Categories
Resources Web Development

Symfony caching resource list and information

Once I started digging into Symfony caching I found all kinds of information all over. I’ll use this page to catalog it all for myself and others. This way I can quickly find what I am looking for.

Cache – From the docs. This is the overall plain documentation about Symfony cache. It covers the following

Configuring Cache with FrameworkBundle
Creating Custom (Namespaced) Pools
Custom Provider Options
Creating a Cache Chain
Using Cache Tags
Clearing the Cache
Encrypting the Cache

The Symfony Cache component – This is the actual caching component documentation. It covers the following.

Installation
Cache Contracts versus PSR-6
Cache Contracts
Available Cache Adapters
Generic Caching (PSR-6)
Basic Usage (PSR-6)
Advanced Usage

Cache pools and Adapters – from the documentation. This covers cache adapters such as Redis and Memcached. It covers the following information.

Creating Cache Pools
Using the Cache Contracts
Using PSR-6
Looking for Cache Items
Saving Cache Items
Removing Cache Items
Pruning Cache Items

Cache items – from the documentation. You need ItemInterface and cache items in order to set expire information on cached items. This link covers the following :

Cache Item Keys and Values
Creating Cache Items
Cache Item Expiration
Cache Item Hits and Misses

Tagged Cache aka Tag Aware Caching – This article explains how tag aware caching works in Symfony. The article is old from version 3.2 when this feature was added.

Categories
Resources Web Development

Symfony 5+ check if user is logged in inside a twig template

Often you may need to know whether a user is logged in or not inside a template to show or not show something. For example you might want to show links to login or register if a user is not logged in but show a link to logout if the user is logged in.

To do this you use is_granted() within a template with one of the following.

IS_AUTHENTICATED_ANONYMOUSLY, IS_AUTHENTICATED_REMEMBERED, IS_AUTHENTICATED_FULLY

<div class="modal-body">
<ul class="nav flex-column">
{% if is_granted('ROLE_SUPER_ADMIN_1') %}
<a class="nav-link" href="{{ path('show_dash') }}">Dashboard</a>
{% endif %}
{% if is_granted('ROLE_USER') %}
<a class="nav-link" href="{{ path('app_logout') }}">Logout</a> {% else %}
<a class="nav-link" href="{{ path('app_login') }}">Login</a> or <a class="nav-link" href="{{ path('app_register') }}">Signup</a> {% endif %}
</ul>
</div>

Using ROLE_SUPER_ADMIN_1 which is something I made up for my own app to check what type of admin the user is. I don’t really like the IS_AUTHENTICATED_* methods, read more about them in the link below if you want.

Link to more information about IS_AUTHENTICATED_* here in  a really old symfony cast I found via google.

Categories
Resources Web Development

How to create a cookie in Symfony 5.0+ and render a template in a controller

First what I wanted to do was create a cookie in a Controller and display a template at the same time. Sort of like when a user visits a page you set a page count or something.

There is more than one way I have discovered over time. Apparently you can use render the same way I show using renderView.

Below is the Symfony Cookie class create method comment/documentation. This is all of the values you can supply when creating a cookie.

 /**
     * @param string                        $name     The name of the cookie
     * @param string|null                   $value    The value of the cookie
     * @param int|string|\DateTimeInterface $expire   The time the cookie expires
     * @param string                        $path     The path on the server in which the cookie will be available on
     * @param string|null                   $domain   The domain that the cookie is available to
     * @param bool|null                     $secure   Whether the client should send back the cookie only over HTTPS or null to auto-enable this when the request is already using HTTPS
     * @param bool                          $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
     * @param bool                          $raw      Whether the cookie value should be sent with no url encoding
     * @param string|null                   $sameSite Whether the cookie will be available for cross-site requests
     *
     * @throws \InvalidArgumentException
     */

If you create a cookie like this :

$response->headers->setCookie(Cookie::create('foo', 'bar'));

Then the cookie will only live/exist until the user closes their browser(unless your browser restores from your last session). You must supply an expires time to make it persist beyond closing the browser. Providing an expires time gives you better control over when the cookie expires due to the above mentioned browser restore issue which will restore cookies that should have died on browser close.

You can also create the cookie then pass it to setCookie() like this.

 $response = new Response();
        $expires = time() + 36000;
        $cookie = Cookie::create($cookieName, $cookieValue,  $expires);
        //$cookie = $response->headers->setCookie(Cookie::create('foo', 'bar'));
        $response->headers->setCookie($cookie);

        $content = "<html><body><h1>Learning symfony cookie creation techniques?</h1></body></html>";
        $response->setContent($content);
        $response->headers->set('Content-Type', 'text/html');
        return $response;

Here I set the expires to a number,  time() returns a linux/unix timestamp and I added 36000 seconds or 10 hours to it. This cookie will exist until the user refreshes their page or clicks a link in 10 hours from creation. However long you want it to live you add that many seconds. Or you could create a date using PHP DateTime as you can pass a DateTime object to the expires position. You then use the methods of DateTime to increase the time to a period in the future and pass the DateTime object after calling the methods to do so.

Side Note : in the above code, you can create a cookie without the $response->setContent() call. I do that with the body tag so that the profiler will show up at the bottom of the page for debugging.

That code goes inside a controller method for the requested route by the way. Usually you use the render() method inside a controller to send a response, which renders the template and sends it in a response. You can also use renderView to do the same thing and capture the value in a variable then use setContent or just make the renderView call right in setContent. I know that works. You can also store the returned value from render the same way.  But no matter how you do it, you must return the response object, the very last line. You can find all the methods of the Response class here in the source code.

If you wanted to render a view which requires variables to be sent you do it like this and capture the output of renderView().


 $content = $this->renderView('blog/display_article.html.twig', [
            'title' => $title,
            'article' => $article,
            'tags' => implode(', ', $tags),
            'tagLinks' => $links,
            'edit' => $editLink,
            'affiliateUrl' => $affiliateUrl,
            'backButton' => $backButton
        ]);

Note : do not just use php setcookie or setrawcookie. The reason is they start sending output headers to the browser, which may interfere with how symfony works. You probably won’t notice in a browser, but you may get errors when testing your controllers with functional tests etc.

Personally I created a huge class which extends DateTime which has all kinds of methods for adding days, hours, removing them and doing other math. I’d share it on github but it has bugs since I wrote it way back in version 5 of php in 2012. Some changes were made to DateTime and I haven’t had time to review them all and hunt down the changes that need to be made yet. I’ll probably do it and add it to github eventually.  But for now I use time() + seconds. It’s not the best solution but it works and I only need this one cookie.

And another person found me more hidden docs about cookies, I wish I had this days ago.

As another note. Any values you put in a cookie you must sanitize before trying to use them in any way since users can access and change regular cookie values.

Categories
Resources Web Development

Symfony 5 how to clear the cache

I can never ever remember where I see anything ever I read entirely too much about entirely too many subjects. I mostly use this site as my own personal google.

To clear all caches
php bin/console cache:pool:clear cache.global_clearer

Symfony docs link to more info.

Categories
Web Development

symfony how to start encore watch

I use so many languages and tools, I can never even remember the most simple things about anything anymore . This is how you start symfony encore webpack.

yarn run encore dev --watch

Symfony docs how to start encore
https://symfony.com/doc/current/frontend/encore/simple-example.html#the-import-and-export-statements

Categories
Software Development Web Development

Where does symfony php framework hide the errors?

I kept saying this over and over and looking everywhere for answers. I finally found this page through googling tons of terms and combos until I found it. I kept thinking my errors would be in the logs I set in my Nginx configs, or even in my PHP configs. But they were continually empty, I was going insane. I seriously blew up on twitter.

BLOWING UP

I thought Symfony was simply suppressing or not passing the errors to Nginx. So the logs are located at the place in the link above from documentation and not in the location you set in the Nginx configs or PHP configs.

By default, log entries are written to the var/log/dev.log file when you’re in the dev environment. In the prod environment, logs are written to var/log/prod.log

What made this confusing was, the docs don’t have a logical link flow when you are reading them trying to learn Symfony. You later find the SymfonyCasts which are better. But what makes it  most confusing is in the docs about configuring Nginx, it even shows the following.

    error_log /var/log/nginx/project_error.log;
    access_log /var/log/nginx/project_access.log;

To me this was showing how to set the error log. This does nothing by the way, not unless nginx itself has an error I guess.

For months I’ve wondered WTF, where are my error logs. I kept putting my app in dev mode so I could debug it via the browser.

Don’t do that!!!!!

To be honest getting Symfony working with Nginx is a pain in the ballsocks. The reason is, Nginx doesn’t pass environmental variables through to php scripts like Apache does/can. If you want that kind of fancy feature you must hack nginx up and use some perl script or something similar. Otherwise with Nginx you must set the environment variables twice, once in nginx and once in shell.

Why would you do that?

Why two locations? Yeah this really angered me and blew my mind at first too. As mentioned above Nginx doesn’t have any easy way to pass the environment variables you set at the Linux server level. This is important with Symfony because you often need to run things like Doctrine on the command line.

So I was setting my Nginx Environmental variables, the app would see them just fine. I’d go to run doctrine or tests and BOOM missing environmental variables like WTF? Or I would set them in the Linux environment, view them with printenv load the app in the browser and Nginx didn’t pass the values to my script. It took a lot of googling to figure that out with lots of trial and failure. To make matters worse, you have to change the environmental variable names in order to run tests so that symfony loads them, otherwise it hides the values.

WTF is happening?

I then found out through experimenting that you had to set the variables for the command line in the Linux environment too. How to permanently set Linux environmental variablees covers how to do that. It’s easier to just Bash script or ansible the entire process with Hashicorp packer than to try to manually maintain it all, setting vars in two different places etc.

So for months I’ve been going insane trying to find my error logs. Today I found the error logs.

Problem solved

 

Categories
Software Development Web Development

Symfony doctrine remove column from entity table

As a newcomer to Doctrine I had to google and dig to figure this out. Explanation here.

Basically if you want to remove a column from a table when using Doctrine and Symfony Entities all you do is remove the variable and the matching getter/setter methods from the Entity, then run a diff.

php bin/console doctrine:migrations:diff

instead of making a migration, this command makes the migration file for you. Then you can run this command to change the table.

php bin/console doctrine:migrations:migrate

What is happening is Doctrine ORM is used to map the Database tables to an Entity. Every Doctrine/Symfony Entity matches 1 table in a database.

Lets view an example. This Entity should be familiar to anyone who created an app using the Symfony maker system. User.php should be located in app\src\Entity\User.php and it looks basically like this.


<?php namespace App\Entity; use App\Repository\UserRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Security\Core\User\UserInterface; /** * User * * @ORM\Table(name="user", uniqueConstraints={@ORM\UniqueConstraint(name="email_UNIQUE", columns={"email"})}, indexes={@ORM\Index(name="email", columns={"email"})}) * @ORM\Entity(repositoryClass=UserRepository::class) */ class User implements UserInterface { /** * @var int * * @ORM\Column(name="id", type="bigint", nullable=false, options={"unsigned"=true}) * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ private int $id; /** * @var string * * @ORM\Column(name="email", type="string", length=255, nullable=false) */ private string $email; /** * @var string * * @ORM\Column(name="password", type="string", length=255, nullable=false) */ private string $password; /** * @var array|null * * @ORM\Column(name="roles", type="json", nullable=true) */ private ?array $roles; /** * @var string|null * * @ORM\Column(name="web_token", type="string", length=255, nullable=true) */ private ?string $webToken; /** * @var string|null * * @ORM\Column(name="api_token", type="string", length=255, nullable=true) */ private ?string $apiToken; public function getId(): ?string { return $this->id;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;

        return $this;
    }

    public function getPassword(): ?string
    {
        return $this->password;
    }

    public function setPassword(string $password): self
    {
        $this->password = $password;

        return $this;
    }

    public function getRoles(): ?array
    {
        return $this->roles;
    }

    public function setRoles(?array $roles): self
    {
        $this->roles = $roles;

        return $this;
    }

    public function getWebToken(): ?string
    {
        return $this->webToken;
    }

    public function setWebToken(?string $webToken): self
    {
        $this->webToken = $webToken;

        return $this;
    }

    public function getApiToken(): ?string
    {
        return $this->apiToken;
    }

    public function setApiToken(?string $apiToken): self
    {
        $this->apiToken = $apiToken;

        return $this;
    }


    public function getSalt()
    {
        // TODO: Implement getSalt() method.
    }

    public function getUsername(): ?string
    {
        // TODO: Implement getUsername() method.
        return $this->getEmail();
    }

    public function eraseCredentials()
    {
        // TODO: Implement eraseCredentials() method.
    }
}

As you can see I have a web token and api token in this class. If I wanted to remove the web token from my database I simply remove the relate variable and getter/setters like so.


<?php namespace App\Entity; use App\Repository\UserRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Security\Core\User\UserInterface; /** * User * * @ORM\Table(name="user", uniqueConstraints={@ORM\UniqueConstraint(name="email_UNIQUE", columns={"email"})}, indexes={@ORM\Index(name="email", columns={"email"})}) * @ORM\Entity(repositoryClass=UserRepository::class) */ class User implements UserInterface { /** * @var int * * @ORM\Column(name="id", type="bigint", nullable=false, options={"unsigned"=true}) * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ private int $id; /** * @var string * * @ORM\Column(name="email", type="string", length=255, nullable=false) */ private string $email; /** * @var string * * @ORM\Column(name="password", type="string", length=255, nullable=false) */ private string $password; /** * @var array|null * * @ORM\Column(name="roles", type="json", nullable=true) */ private ?array $roles; /** * @var string|null * * @ORM\Column(name="api_token", type="string", length=255, nullable=true) */ private ?string $apiToken; public function getId(): ?string { return $this->id;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;

        return $this;
    }

    public function getPassword(): ?string
    {
        return $this->password;
    }

    public function setPassword(string $password): self
    {
        $this->password = $password;

        return $this;
    }

    public function getRoles(): ?array
    {
        return $this->roles;
    }

    public function setRoles(?array $roles): self
    {
        $this->roles = $roles;

        return $this;
    }

   

    public function getApiToken(): ?string
    {
        return $this->apiToken;
    }

    public function setApiToken(?string $apiToken): self
    {
        $this->apiToken = $apiToken;

        return $this;
    }


    public function getSalt()
    {
        // TODO: Implement getSalt() method.
    }

    public function getUsername(): ?string
    {
        // TODO: Implement getUsername() method.
        return $this->getEmail();
    }

    public function eraseCredentials()
    {
        // TODO: Implement eraseCredentials() method.
    }
}

Now when you run the migrations:diff command a new migration file will be created. Inside that file will be the queries to make the changes in the “up” method of that migrations file. In the “down” method you will find the code to restore the database and add the column back, which will also update the Entity class.

After you run the diff you run a migration. Running the migration changes your database to match your changes in your Entity class.

Categories
Resources Web Development

How to output Symfony environmental variables

I couldn’t remember where the hell I had seen this in the documentation so I am linking to it here. Why?

Because it absolutely isn’t where you would expect it to be like listed under Environmental variables. Hell no why list that shit there that’s all logical and shit.

It is actually under configuration all the way to the bottom under “List environment variables.” Would be so nice if that was in the Enironmental Variables page instead or a link to it or mention of it.

Categories
Resources Web Security

Symfony doctrine database secret configuration links and resources.

Storing secrets for Symfony applications – some ideas how to approach the topic