A CRM on Symfony for Oxford Business Group

How we built a CRM on Symfony


From a simple search page to a high-grade CRM application in a year? That’s what we’ve been doing for Oxford Business Group using Symfony.

OBG is a global publisher and consultancy that has offices all around the globe. We were challenged to create the application that would allow the OBG staff to get timely updates on projects.

In this article, we will tell you how a CRM system on Symfony may look like, what goals it reaches, and what features includes.

CRM: goals

Likewise many other custom CRM systems, a CRM application for Oxford Business Group includes different search pages, forms for creating, editing and removing content, a users permissions system, a statistics dashboard, a scheduling system, etc.

We were supposed to create the application that should include the following functionality:

  • Data processing (editing / creating / removing information about companies, meetings, contacts)
  • A permissions system (an access to a project)
  • Search
  • Workflow features / Easy and comfortable layouts

Data Processing

There is a lot of stored information about companies, contacts of these companies, meetings. We designed the special pages for viewing, searching, and editing information. We have the separated blocks with scheduled meetings, and we have other blocks for a special type of content that contains all information connected to a meeting: when it was held, what companies took part in the meeting, different notes of a meeting’s assignee.

Meeting notes autosaving 

Having a meeting considers taking a lot of notes. We developed the special feature that saves them for a manager. The manager doesn’t have to click on the ‘save’ button: all that he/she writes is saved in real time. This solution defends the manager against situations when he/she writes a lot of information and accidentally closes a page or something goes wrong with the internet connection, and when they try to save information by clicking on the button - a browser returns an error and all data is lost then.

We also worked on the optimization of our code, and this solution never hangs a server. We assume that this is a good practice - to use real-time saving for important information that users enter using their browsers.

A log system for calls and emails

Calls to different companies, negotiations, scheduling meetings are typical daily routine for managers. Of course, managers need a special place where they can find information about a company or add a new one, find a contact person and log them. Logging information about calls and emails is important because it helps to navigate through a big amount of companies and change priorities of calls or just separate companies that are not interested in the OBG services.

A log system for calls and emails
A log system for calls and emails

The latest meeting and a date of the latest getting in touch (with a company)

If you design a database that consists of a big number of tables and different relationships it could be difficult to produce fast queries. A typical situation here is a case when you are forced to find a way to denormalize your database because a lot of JOINs can’t work fast. The best way to solve it is using some search tool like Solr or using another relational database for saving indexes. At this moment, we don’t know whether we will use another kind of search or not in the future. So, using  Solr looks like overhead because we just need to optimize search on the one page for a few parameters. And our solution is very typical.

We added special fields to the ‘companies’ table that stores information about when these companies were contacted the last time and a date of the latest meeting. We created a special listener that fires special methods from our service class for updating these values. The architecture is simple and clear. One listener class listens to Doctrine events like ‘post update’, ‘post remove’, and ‘post delete’. Another service class has complex methods of getting data of the last meeting / a date of the last getting in touch, and updating a company’s record. Using something like this is very typical because we have no alternative solutions. Solr is nice, but we don’t need a search tool at this moment. We just need to optimize two queries. Using cron also can’t help us because we need to update records in real time. So, we found a rather good solution that helps us to save time and release very tricky logic for searching last meetings dates.

The permissions system

Like any other CRM, the CRM for Oxford Business Group has several types of users and the flexible permission system. Of course, each group of users is granted with its own combination of permissions.

Users roles 

Next point is usage of a roles’ system. For example, a user can be an ‘administrator’ in one project, and in this case, he/she can do all operations with content in this year. In another project the same user can be an ‘editorial manager’, and in this case he/she can’t remove any data from this project.

And the last layer of the permissions system is usage of Symfony FOSUserBundle roles. This layer is not directly connected to user permissions in projects but it defines what global operations are available for the user: viewing administration pages (like a page where admin can create a new user) or something else.

User Access Log

Administrators should have some statistics about using the application. We added the special functionality that can help admins with that. When the user logs in CRM it (CRM) fires a special method that saves a few details into a database: a date of the latest user login and an updated login counter. This approach helps us to understand who's not active and who’s using the application a lot.

Select Project Form 

The first step of optimization is the decrease of selection. All in all, if a user is forced to search information by looking through a lot of data, it takes more time than if the user has a small data set instead. We decided to add a form where the user can select a project. It means that the user works with data connected only with a selected project. This solution allows us to use our permissions system and makes the CRM application faster.


Searching information should be easy and simple. We added all key fields that are usually used by managers in their search requests to forms. There are a lot of tricky fields that run complex queries and filter information in different layers. For instance, a user can find all companies that have advertising contracts in different projects or have no meetings yet. Also, we had a lot of requirements regarding queries’ maximum execution time, and we spent some time on optimising our requests.

Symfony CRM system search
Symfony CRM system search

Saving search 

From time to time, users search for information by using the same parameters. We added a functionality that can help users to deal with this. So, you just select parameters one time and click the ‘save search’ button. It saves your searching parameters and makes it available on a special page. Also, you can make your saved searching parameters available for other staff. It’s convenient and gets you rid of routine operations each time when you need to get results by parameters that you already have used.

We’re using our custom service for that, and if we have new search pages in future, we will be able to add the saving search functionality for these new pages. When we designed this feature we decided to write our custom service for parsing URLs. Why? If we change a site URL all saved searches won’t be available, because results depend on a site address. So, instead of using hardcoded links to pages in our solution we added a URL parser, which saves GET parameters to JSON. This looks pretty fine now, and can’t be affected if we move our app to another domain address.


This is a common practice to use some keywords to mark similar records. A lot of different CMSs and frameworks provide tagging functionality. There is a lot of bundles in Packagist, but we chose to write a custom bundle for our needs. This solution is more flexible for client requirements, and has no extra logic or heavy queries. Our approach allows us to integrate tags bundle with the application permissions system and searching functionality. Not all users can create new vocabularies or edit existing tags. A set of terms that the user can add to different content depends on a selected project. Some vocabularies can appear in one or a few projects. If we had used a third-party bundle for tags we would have got overhead and a non-flexible solution. 

Workflow features / Easy and comfortable layouts

Managers have a very busy day. All that they need is to open one page and get all information about a company, open related information like meetings, list of calls or “outlook file” by making just one click. So, we tried to take this into consideration and worked a lot on pages’ structures, layouts, and different control elements.

Company Wizard

Let’s consider a very simple use case. A manager tries to find the company by using a search page but the page returns no results. On the search page’s form, we placed the button ‘Add Company’. This button provides a method of creating a company without any details excluding a company name. If a user clicks on this button, a modal window will be opened where the manager can type a name of a new company, save and add other details or simply save and continue working with the search page. We used the modal windows API provided by Bootstrap, added custom logic. Very simple, but it helps managers to avoid extra clicks.

Company Search
Company Search

Interactive calendar

Our application has a mean of managing scheduled meetings. We used FullCalendar Bundle to release this feature. How does it work? You open a page and see a grid containing days of a current month. Each cell contains a list of meetings scheduled for this day. Meetings have different colors because a meeting can be of different types. If you click on a cell the application opens a modal window that allows you to add a new meeting.

Interactive calendar
Interactive calendar


How can we have a look at information connected with a particular project? What if I want to select companies in one project and select companies that have already signed a contract with us? We provide a way to do that. The special page ‘dashboard’ displays information about companies’ statuses in different projects.



Shout out to those who made it this far! We had the quick observation of the main features implemented in this CRM, and now you are all set to develop your own CRM with Symfony. 

And we’re always here for you to consult and just chat.

You might also like

Drupal 8 core and Symfony components

Drupal and Symfony

Adding Symfony components to Drupal 8 had the biggest impact on its development. Here you can find code snippets that will help you feel the difference between the “clear” Symfony and Drupal 8 solutions