Ad Blockers and why your innocent files may be getting blocked

The other day I noticed a new image was not displaying on a site I was working on. Doing an inspection of the image, I could see that the img src path was correct, and the file was loading fine. But there was also a style="display: none !important;"  which was being added to this <img> tag. Removing that style resulted in it coming back instantly.

I also noticed a Failed to load resource: net::ERR_BLOCKED_BY_CLIENT message in the console.

Turns out the ad blocker I was using, AdBlock Plus, blocks anything here:

/media/ad/*

The path to my blocked file happened to be here:

/media/ad/76/ad763f1c400bea2c54c3d14008a968d331720090.jpg

That path was generated automatically by a class in a bundle I created for managing file names and paths in Symfony: MassMediaBundle

It creates a filename using a hash algorithm, and splits letters off the first part of the name to create folders. In this case, it just happened to create one which started with the letters “ad”, causing AdBlock Plus to filter it when preceded by a folder named: /media/

It seems that /media/ad/ was a popular enough destination for advertisements that it got added to the AdBlock filter list, which you can have a look at here:

https://easylist-downloads.adblockplus.org/easylist.txt

I see 3 possible options:

1) Ignore the problem, and allow AdBlock users (millions of people) to not view your images.
2) Tell your users to whitelist your site in their ad blocker, and hope they will comply.
3) Rename the filtered folder(s) to something unlikely to be blacklisted.

Option 3 is the route I ended up taking in this case.

Just a friendly reminder to test your websites in popular ad blockers, and to consider the effect your upload paths can have on files being filtered by them.

How to use jQuery Tag-it plugin with a Symfony form

This is Part 4 in a series on setting up Tags in a ManyToMany association with an Entity in Symfony. You can read the previous three articles here:

  1. Managing Tags in ManyToMany association with Symfony
  2. Setting up Form Collection for Tags in Symfony
  3. Using a Data Transformer in Symfony to handle duplicate tags

By now, you should have your Product and Tag entities set up, along with your forms, and a data transformer for preventing duplicate tags from being added to the database. At the end of the previous article, we also created a controller which renders the submitProduct.html.twig template that we’ll be using for displaying our form to the world.

As mentioned in the previous article, we are going to need to add some JavaScript to add/remove tag.name  inputs for our form. There are multiple ways you can do this, and the Symfony documention on Allowing “new” Tags with the “Prototype” shows you a good example.

In this article, we will be using the jQuery Tag-it plugin, which allows you to have a tag input box on your page which works something like this:

    You can read Tag-it’s documentation for more information on it. For this tutorial, go ahead and download/unzip Tag-it, and copy these files:

    css/jquery.tagit.css
    css/tagit.ui-zendesk.css
    js/tag-it.min.js

    Into your Symfony web assets folder:

    web/css/jquery.tagit.css
    web/css/tagit.ui-zendesk.css
    web/js/tag-it.min.js

    Make sure you also have jQuery and jQuery-ui included in your template, as they are required by Tag-it.

    Ok, now how to set up this plugin with our Symfony form?

    Remember in the Symfony example using prototype, that you are replacing __name__ with the index of the next item in the array:

    product[tags][__name__][name]

    Using a find replace on __name__ you can create new inputs for your form:

    The way that Tag-it works is to add a hidden form input for each tag. It has a fieldName option which allows you to set the name you want to use for your inputs. You might think of doing something like this there:

    That will actually work to add your tags, but will not play nicely when you later remove a tag from the middle of a group of tags associated with a Product, and add another to it for example. The index of the item in the array needs to be set in the field name, so that it knows exactly which item to remove/add.

    So how to increment the field name’s index in Tag-it?

    In the Tag-it documentation, you will notice there is a beforeTagAdded event. We can use this to increment our index, and reset the Tag-it fieldName value each time a new tag is added. Here is a basic example:

    Test that by typing 3 new tags into your Tag-it input box: car, Japanese, import

    And you’ll see that Tag-it adds the hidden inputs to the form just we as we want them:

    If you prefer not to hand code the field name value in, but want to use Symfony’s form prototype instead, you could do something like this:

    Notice the {% do form.tags.setRendered %} added in the first line there. You will need this if you are using Symfony’s {{ form_end(form) }} to end your form. The reason is that form_end will output anything it thinks is missing from your form. It will believe that 'tags' are missing since we are actually creating the tag inputs ourselves. So we tell it that form.tags  are already rendered to prevent that.

    Another option would be to just close the form yourself:  </form> . But don’t forget to render the CSRF token (or anything else your form requires) in that case.

    The final result of your form might look something like this (using Bootstrap here):

    If you want to edit the tags of an existing Product, you could pass that Product to the view in your controller, and then output all of its tags inside the unordered list like so:

    The final result is a setup which meets the requirements outlined in the first article of this tutorial.

    I hope that this has given you some ideas on ways you can manage tags for your entities in a ManyToMany association in Symfony. If you have any questions or suggestions, please leave them in the comments!

    Using a Data Transformer in Symfony to handle duplicate tags

    This is Part 3 in a series on setting up Tags in a ManyToMany association with an Entity in Symfony. You can read the previous two articles here:

    1. Managing Tags in ManyToMany association with Symfony
    2. Setting up Form Collection for Tags in Symfony

    In the last article, I talked about the problem of duplicate Tags being added when creating a new Product. Here, I will show one way to handle that using a Data Transformer.

    First, we will create the data transformer:

    Your data transformer implements the DataTransformerInterface, which requires these two methods:

    transform  allows you to alter the data going to your form. We don’t need to change this, so can just return the tags as is.

    reverseTransform  allows you to alter the data submitted by your form. This is where we want to make our changes.

    In the constructor, the ObjectManager  is injected for checking a Tag’s existence in the database.

    In the reverseTransform  method, an empty ArrayCollection is created to store all tags we’ll be returning. For each submitted Tag, it checks for its presence in the Tag repository. If present, we replace the form submitted Tag with the one already present in the Tag repository. If not, then we allow the new form submitted Tag into the $tagCollection .

    Finally, the $tagCollection  is returned.

    You can do any other filtering you like there. But for the purposes of this tutorial, we’ll keep it simple like that.

    The next thing we need to do is make a few changes to our ProductType:

    We’ll be using the ObjectManager  and our newly created TagsToCollectionTransformer :

    The ObjectManager  is injected in the constructor. (We’ll create a service for our ProductType class later)

    We also get 'tags'  from our form builder, add our transformer to it using the addModelTransformer  method, and pass in the  ObjectManager  dependency to our TagsToCollectionTransformer .

    Finally, we create a service for our ProductType  class which injects the ObjectManager  dependency to it:

    Our form is now ready to roll.

    Let’s create a controller for submitting new Products:

    This should be pretty self-explanatory if you’re familiar with controllers, routes, form submissions, and persisting objects to the database in Symfony. Basically we just create our form with a new Product  object, and have the form handle the Request. If a form was submitted, and is valid, we persist the Product to the database. Finally, we render the submitProduct template which will be created later.

    Note that while it’s not actually required to check $form->isSubmitted() , it is recommended per the Symfony best practices on form submissions for readability:

    Second, we recommend using $form->isSubmitted() in the if statement for clarity. This isn’t technically needed, since isValid() first calls isSubmitted(). But without this, the flow doesn’t read well as it looks like the form is always processed (even on the GET request).

    The last thing to do is create the Twig template submitProduct.html.twig which will display our form. Since we’ll be adding and removing Tags from Products, we’ll need some JavaScript to add/remove our tag.name  inputs for us. You could accomplish this using the example in the Symfony documentation, but in the next article, we’ll be using the jQuery Tag-it plugin instead.

    Next article: How to use jQuery Tag-it plugin with a Symfony form

    Setting up Form Collection for Tags in Symfony

    This is the second article in a series on setting up a ManyToMany association for tags in Symfony. You can read the previous article where we created our Product and Tag entities here:

    1. Managing Tags in ManyToMany association with Symfony

    Now that our entities have been created, it’s time to create our forms. Basically, what we want here is a Product submission form where you can add/remove a collection of Tags which will also be associated to our Product. First, let’s create a Tag form which will be added to our Product form later to collect tags.

    We only need a 'name'  for each tag.

    Next, we’ll create the Product form:

    We add 'tags'  here to the builder as a CollectionType , and set 'entry_type'  to TagType::class . That is the TagType form class we created above in TagType.php, with ::class to get the fully qualified class name.

    We set  'allow_add'  and 'allow_delete'  to  true  because we want to be able to add and delete the associations of Tags to each Product.

    Finally, I set 'required' => false , because I want adding Tags to be optional.

    We haven’t gotten to the point of setting up anything in our Controller or templates yet, but I want to show you what happens now if we leave things the way they are so far in this example.

    Currently, if you added one Product (BMW) with the following tags:

    new product BMW

    When you check your database, you would see this in your tables:

    product

    Product table

    product_tag

    product_tag table

    tag:

    tag table

    Great. Now imagine adding another Product (Honda) with a car tag again, and see what happens:

    add product Honda

    Check your tables and you will find:

    product

    product table 2

    product_tag

    product_tag table 2

    tag

    tag table 2

    As you can see, it is adding duplicate Tags to our tables. However, rather than adding an unnecessary duplicate car tag (tag.id: 3), we want the Honda (product.id: 2) to be associated to the original car tag (tag.id: 1) in the product_tag table. So how can we accomplish this?

    The method which I decided on was to setup a Data Transformer which checks if a tag already exists in the database. If so, it replaces the tag submitted by the form with the one already in the database. This way, the Product will be associated with the original tag in the database, and no new duplicate tag will be added.

    I cover this in the next article: Using a Data Transformer in Symfony to handle duplicate tags