Transcription
Good evening! Thank you for coming. With the last presentation, I bet everyone is eager to get closer to the after-party and have a beer. Today, we have a light talk about broadening horizons, practical expansion of consciousness—beer would definitely help here.
My name is Pyotr Myazin. I have been working in the PHP ecosystem for quite a while and continue to do so. I enjoy it and host a podcast called "Five Minutes of PHP." Does anyone listen to "Five Minutes of PHP"? Great! For those who don’t, subscribe to the Telegram channel; it gets updated even more frequently. I work in logistics.
What kind of logistics? Our company deals with international freight transportation in the B2B sector, so we handle large container shipments as well as small parcels. Our focus in PHP development is on document management in the corporate environment, specifically in B2B.
Today, I want to talk about how we integrate with 1C. It's a program that is practically in every corporation in Russia, and we have to integrate with it as well. I will share my experience, but I won’t be discussing online stores or e-commerce, as I have never dealt with that. However, the technologies I will talk about can be applied to e-commerce, and I think it might be useful for you to know.
Let’s start with a brief overview of 1C. What’s inside 1C? It’s a program where we have various screens, forms, and data. The data stored in 1C is categorized into metadata objects. There are directories, such as lists of cities, countries, and the goods or services we provide.
Then we have documents, which are a different type of data—bills, acts, and possibly some letters or documents related to our subject area, like shipping invoices. An interesting type of data is registers. Registers are a bit more complex to explain, and I actually recommend everyone to read a bit about registers in 1C; it might come in handy for your PHP applications.
For example, historical data like currency rates or prices on a specific date can be stored in registers. In our PHP application, we have ORM models, and that’s all good. Directories map to our PHP code, documents do too, but registers are trickier. They can be compared to event history or an event-sourcing model.
We realized there is a technological connection between our PHP application and 1C. We can draw parallels in PHP with various other data that we might not want to synchronize, so let’s focus on what we do want to synchronize.
We have managers who work in our great software written in PHP through a web interface, managing corporate documents and freight transportation. They don’t want to deal with 1C; it’s inconvenient for them. Plus, working in two programs is not enjoyable for the accounting department, which is entirely in 1C.
We understand that we have the same metadata objects—these data objects are needed in both systems. We would like them to appear automatically through synchronization.
We analyzed what we want to synchronize. We have a directory of our counterparties, which includes clients, contractors, contracts with them, and the services we sell. The main accounting documents are bills and payment acts. There are many other interesting documents that we don’t want to synchronize; they are not of interest to the managers in the PHP application, like various accounting intricacies or the employment acceptance document, which is in 1C but not needed in the PHP database.
We chose to synchronize the important entities and data for us. How do we transfer them from one system to another? If you go to the 1C website and look at the documentation, there’s a page with a bunch of XML, HTTP, and various web services.
Let’s try to categorize them. I divide them into three main categories. There are classic technologies like COM, DBC, and ODBC, which is universal access to databases but is quite inconvenient.
Then there’s the classic approach that almost everyone uses—file exchanges. One system generates a file, uploads it to a shared disk or FTP server, and the other system checks for the file every hour, reads it, and loads it. This file exchange can be in any format we want, like XML or CSV, but it’s not very convenient because it relies on a common folder.
The modern approach that we all know and love is HTTP calls, particularly RESTful APIs. Let’s explore this direction.
Another interesting question, besides what transport we will use, is the direction of data flow. Who initiates the exchange? Where does the new document or new directory element originate?
It turns out that for some documents, we can determine that they always originate in the PHP application. For other documents, like a payment from a bank client, it always goes to 1C first, and then we would like to see that payment in the PHP application so that the manager knows the client has paid and they can proceed with the freight.
There are also documents with bidirectional synchronization; they can originate in either system. We have unique identifiers (UIDs) that help us here. By the way, there was a great talk about UIDs today; did anyone listen to Valentin?
Fortunately, 1C uses UIDs as identifiers from the start, so synchronizing in this bidirectional mode turned out to be quite simple. Everything works well.
Now, let’s get specific. I want to send some data from the PHP application to 1C via HTTP protocol, and it’s even better to use the REST approach, which everyone knows and loves.
It turns out that the 1C platform has a built-in REST interface. Everything in 1C—documents, directories—is immediately available through a ready-made API called 1C. You can read individual entities, lists of entities, make selections based on conditions, and you can even write to 1C using this interface, which is exactly what we want to do from PHP—send a document to 1C.
This is a true REST interface that we are all used to. All HTTP verbs are supported as they should be, and we know how to work with them in PHP.
In addition to data exchange, the 1C REST interface also allows for command execution. This is an interesting topic related to the Common Segregation Principle. There are requests like queries and requests like commands. Commands perform something more interesting and heavier.
1C has a number of built-in commands that we can call through the 1C REST interface. They are ready to be programmed; you don’t need to do anything. If you know you need to perform a document processing or distribution from the PHP application, you can do it with an HTTP call to one endpoint.
The 1C REST interface allows us to send requests over HTTP. What’s inside this request? What URL do we go to, and what’s in the body?
1C chose the OData protocol, specifically version 3.0. If you go to the OData website, you can read that they position themselves as the best way to create REST interfaces. Your REST interfaces will be standard and convenient.
If you are currently writing REST interfaces without touching 1C, take a look at OData; it might be an interesting technology for your project.
Originally, OData was an initiative by Microsoft in 2007, and later the protocol became open and is now developed by a non-profit organization or community. You can read the specification on the OData website.
Data can be transmitted in XML or JSON; we use JSON. The website has links to reference implementations in almost all programming languages, including PHP. They recommend a package that you can find on Packagist, and there are even wrappers for specific frameworks.
There are articles on Habr that are quite good, and if you search on Packagist, you’ll find a lot of packages with a significant number of downloads. This means that people in the PHP world are using it.
I mention this because it’s not some obscure technology; it’s a well-established technology that they used as the basis for their interface.
When we started applying it, I noticed interesting parallels with GraphQL. I had not heard of OData before, but I had heard of GraphQL; it’s a popular technology. By the way, there will be a talk about GraphQL in PHP tomorrow—be sure to come!
We can form a request from the client for a structure we want to receive from the server, including nested and related data.
It turns out that with OData, we can do something similar. We can make clever selections with different conditions and receive an object and its nested entities in one request.
The topic of data versus GraphQL is not new; there are many articles online comparing them. I personally call OData "GraphQL on a budget." GraphQL is more interesting and powerful as a technology, but for our tasks, OData fits perfectly.
Let’s look at an example of how these requests look using the protocol. I send a GET request to a certain URL on the web service—more accurately, an HTTP service from the perspective of 1C. I pass a query parameter to filter the list of goods that meet this condition.
For example, I want to select all counterparties without any conditions, but only two fields: the unique key and a description of the counterparty.
The response from 1C looks like this: we get a JSON array with multiple objects, each containing two fields: the unique key and the description of the counterparty.
It’s easy to parse the JSON in PHP. We loop through it and do what we want—save it to our database or something else.
There are many query parameters. We looked at filters for selection conditions and the Select parameter to choose specific fields. There are also Top, Skip, Count, and sorting options.
In general, you can request data in the format you need and specify exactly what data you want. If we look specifically at filters, we can use logical operators like AND, OR, NOT, and combine different conditions.
It’s also handy to use arithmetic operations directly in the selection conditions—multiplying, dividing, adding, or subtracting. Honestly, I don’t know why this is needed; I’ve never used it.
String operations can also be performed, like searching for substrings, beginnings, or ends of strings. This is also useful, so the query language is quite flexible, with many different operators and built-in functions that can be passed in the URL to get the needed data.
There’s a great operator called expand in the URL. If I write expand, I can get not only the main object I retrieved but also some nested records in one HTTP request, solving the N+1 problem.
For example, if I want to get a list of counterparties, and I have 1,000 counterparties in the database, I can also retrieve their main contracts. I write "main contract," or I want to get all counterparties and immediately expand all nested records.
The response will come as an array of objects, each representing a counterparty. We requested two fields: the unique key and the description.
To avoid pulling unnecessary data, we can specify which fields we want to extract from the nested objects. This is like a minimal GraphQL; we can compose a request to pull only what we need.
If I want to get the card of a specific entity, I know its unique identifier. As I mentioned, everything is built on UIDs. If we have a UID, we can read the entity card by making a request to a specific element.
The POST method works for creating new records, and we can pass the necessary UID identifier when creating a new record. This is exactly what we need for building this distributed exchange, where objects can originate in both systems in any order, and UIDs save us.
Another interesting nuance is that many documents or entities have so-called tabular parts. For example, when I issue an invoice to a client, I have a header with the client’s ID, the date of the invoice, and the invoice number.
In the middle of the invoice, there’s a table with our services: loading a truck for 1,000 rubles, freight transportation for 5,000 rubles, unloading for 1,000 rubles. This tabular part is an array of nested rows.
To send such a document to 1C, we don’t need to make one request for the header and then N requests for each line. Everything is recorded in one HTTP request with a nested JSON array. It’s very convenient and, most importantly, transactional.
Now, about writing data. Suppose I read something from 1C using a GET request, and on the PHP side, I want to manipulate this data and write it back. Even if it’s not a user action, but some background task, what if while I’m manipulating it, another client is also writing?
I want to use a PUT request that updates everything. There’s a well-known method called optimistic locking. There’s also pessimistic locking and a few others.
In 1C, there’s built-in support for optimistic locking in the REST interface. With HTTP headers, I can specify which version of the object I last read, and now I’m updating it. 1C will check if there have been any changes compared to that last version. If there have been changes, my request will fail with an HTTP error code, possibly 400.
This built-in check is great; you don’t need to program anything. It’s very convenient.
The 1C REST interface is built on a protocol with a lot of documentation and libraries. In fact, from PHP, you don’t necessarily need to download any libraries or search for packages; you can make a simple HTTP request to download some data using `file_get_contents`.
That’s the integration.
So, that’s the 1C REST interface, and it’s automatically built on the data in 1C. But what if we need something that the REST interface doesn’t provide? We want to write something to 1C or read something that’s not in the REST interface.
Then we move on to a deeper technology called HTTP services. In 1C, we can write a controller, essentially a regular controller like we do in our PHP projects. A controller is a method or function in the 1C language that accepts a request object, performs some business logic, and returns an HTTP response.
Why might we need this? For generating PDF documents, for instance. We can easily create PDF documents and Excel files with PHP, but in 1C, there are ready-made print forms for invoices and other documents.
It would be great to make one HTTP request to 1C and say, "Return me the invoice," so it comes back directly to the PHP application. Then the manager can do something with that invoice, like send it to a client via email.
We also use electronic document management between companies. We can set up electronic document management so that legally significant documents, particularly accounting documents like closing documents and invoices, are sent with an electronic signature through a special operator.
The famous EDO operators are Diadoc and Sbis; we use them. They all have SDKs available, and we started integrating through APIs. There are also ready-made solutions for 1C to work with EDO, like Sbis.
A great solution is to use the ready-made 1C solution. If a manager wants to send something through our PHP application, we create a button for them to send it. The PHP application makes a minimal call to this HTTP service in 1C, where all the logic for connecting to operators, signatures, and encryption is implemented.
This simplifies our lives.
Perhaps you have specific procedures or functions in 1C that your 1C team has programmed, either in-house or outsourced. You need to call these functions from the PHP application.
As a small aside, here’s how it looks in 1C. This is a router interface where I describe a template string and say, "Please send requests to this string."
This is a typical router like we are used to in PHP applications. Through this interface, the controller is very simple, with imperative code. What I like about 1C code is not just that it’s in Russian, but that it’s imperative and straightforward.
Any PHP programmer, without knowing 1C, can read it. There’s an incoming request object, a couple of response objects, and some business logic. In the end, there’s a 200 response, meaning we can open 1C and just visually check if everything is written correctly.
This is what we wanted from our integration with the 1C team. The 1C language is excellent for reading, making it accessible for any PHP programmer.
Now, let’s consider the reverse: receiving from 1C to PHP, where 1C is the initiator of the exchange. The 1C platform should call our PHP code via HTTP.
It turns out that 1C has a built-in HTTP client. Writing an HTTP call in 1C is quite easy; any 1C developer can handle it. You can read the code and see if it’s calling the correct URL, checking for fingerprints, and so on.
It’s easy to develop and test.
For example, we usually work with PHP through queues and jobs to ensure reliability. If a remote service is currently unavailable, we might need to implement exponential backoff for requests.
If we write a direct HTTP request in 1C and our PHP application is down or there’s a network issue, everything will be lost.
In the 1C ecosystem, there are ready-made solutions for message delivery using a protocol called "bus." The term "bus" hints at some enterprise integration services, known as integration plans.
The task is to ensure that 1C understands which objects have changed and only sends the changed ones. You can set this task for your 1C team or outsource it.
I’ve seen on YouTube, if you don’t know, I can suggest you look into the bus or integration plans. They can handle it; all the tools are ready.
Another approach is to minimize the code written on the PHP side. For example, if a new document is created in 1C, we can pre-formulate a JSON request specifying which data we will need.
Then we can re-agree and re-program on the 1C side. Alternatively, we can ask them to trigger a small webhook in our PHP application for each change in these objects, sending only the UID of the document that changed in 1C.
We can then queue it up, and when the queue processes, we can read the data from 1C using the 1C REST interface. This way, the programming effort is minimal; we just need to make that micro HTTP request, and we can read everything ourselves.
This is called a pull model, which is much easier to maintain and communicate.
Of course, this webhook call must also be reliable so that it doesn’t get lost in the process. Some integration plans might need to be connected.
As we develop this idea, we should simplify everything as much as possible. We maximize the use of the pull model.
We also encountered some challenges. For example, we have a directory of cities in 1C. If a new city is added, we could write that only the new city should come to PHP.
But what if the address of a warehouse changes? We need to update that. If other parameters change, we don’t need to update them.
In reality, there’s a lot of logic that might not be necessary. In our B2B logistics, we have 200 cities in our directory. We can read all 200 cities from the 1C REST interface with one GET request.
If a new city appears, we can read it without overloading the system because reading a directory of 200 items works faster—less than a second, with no load at all.
We removed all the if statements about whether to update or not. We maximize the use of 1C and minimize interaction with the 1C team, taking everything into our own hands.
I presented this talk to our 1C team, and they said, "What? We need to write everything in 1C!" It’s clear that everyone wants to pull the blanket over themselves.
In conclusion, there are great tools if you need to exchange data with 1C, like the 1C REST interface. If that’s not enough, there are HTTP services, which are also great and convenient.
Finally, if you need to do a micro-integration tomorrow, you can literally use `file_get_contents` to download the salaries of all your colleagues and ride off into the sunset.
My name is Pyotr Myazin. You can vote for my talk using this QR code, and here’s a link to the PDF presentation on the "Five Minutes of PHP" website. Don’t forget to subscribe to the podcast or write to me on Telegram.
Now, I’m ready to take your questions. Thank you very much!
What a self-sufficient speaker! Riding off into the sunset with the salaries of colleagues— I especially liked that.
Alright, folks, raise your hands if you have questions. I see two hands. Please go ahead.
Hello, thank you for the talk. I have a question. You described different integration methods between 1C and PHP, but what about authentication between the two systems? What do you use, and what do you like?
In the 1C REST protocol, there’s built-in Basic authentication. When I’m on the 1C side, I need to enable it and specify a user and password or select a user from existing 1C users.
Authentication can be done through HTTP headers. When we make HTTP requests from 1C to PHP, you can do it however you want—through HTTP headers or by including a secret token directly in the URL.
We use headers for this, like `Authorization: Basic ...` and others. For HTTP services, it’s not very suitable for 1C, but you probably know better than I do since I’ve only skimmed through it.
Yes, there are various technologies, but I don’t need them for my tasks because they seem simpler. Basic HTTP authentication is sufficient for us since it’s all within the enterprise’s internal network.
Great! I see another hand, and then I’ll take one more.
Just a reminder: when you scan the QR code and vote for the talk, it helps the program committee shape the next PHP Russia program.
Thank you for the talk! A small question about the OData protocol. You compared it to GraphQL, but it has a fatal flaw: it can’t extract an entire tree in one request, only a predetermined number of levels. Can OData do that?
That’s a good question. I haven’t explored that aspect. In our integration, we only went down one level.
Hello, thank you for the talk. I have a question. It’s good when there are 200 items in a directory, but what if the task is to export a large number of items, like several hundred thousand from the information register? Does it handle that, or do you use pagination?
Yes, it handles it. I haven’t had tasks to constantly read hundreds of thousands of records, but at the beginning of the project, we needed to extract all data for the last three years.
We did this through the interface on the PHP side, making one HTTP request to get a huge package, like 20 MB for 200,000 records. It comes through without hanging.
Then we made 200,000 separate HTTP requests to read each item we wanted to download. This worked at a speed of about 1,200 records per minute.
We used queues on the PHP side, creating a separate job for each record, and it processed everything without any errors or interruptions.
We have another question from the chat. I see a hand raised.
A question from Alexander Ivanov: What are the advantages of your approach compared to data schemes like SOAP?
I didn’t mention SOAP in this talk; I honestly forgot about it. We have used SOAP, and I showed how I categorize the methods I prefer.
HTTP is more pleasant because it’s easier to debug from the PHP side. SOAP has a PHP extension, but it’s more cumbersome.
I would say that HTTP is closer to the second method I mentioned. It’s also over HTTP, but inside XML with specific formats. Some say it’s slower, but I haven’t encountered performance issues.
It’s just that working with SOAP is somewhat inconvenient.
Why is there such a strong desire to implement all logic exclusively on the PHP side, minimizing calls to 1C programmers? Wouldn’t it be better to use a bidirectional queue where both sides exchange only the data they generate?
My opinion is that we should focus on one side. We are a PHP team, and it’s more convenient for us to work in PHP.
We have 1C developers on outsourcing, and I have to ask myself whether to create a ticket for them or write a specification for the 1C developers.
It might even be cheaper to go with 1C developers because they are professionals, but I still prefer to work with my team.
This is a matter of communication, speed of feature delivery, and business value. If we were a 1C team, we would be at a different conference saying that PHP developers are driving us crazy.
Let’s live in harmony! The problem is that everyone pulls the blanket over themselves, but we can agree and write one system together.
These communications also consume time and money. A bidirectional queue would technically work, but it’s an organizational question.
I see another hand.
Thank you for the talk! I have a question. I understand that you used 1C functionality in your project to avoid writing some parts in PHP. What’s the volume of data in your system, and how relevant is the performance issue for you?
That’s a delicate point. At the conference, I’ll share a secret: our system is not a corporate one. Our clients are freight delivery companies, not millions of users like the Russian Post.
We deal with thousands of companies and businesses, and the number of documents and data we store in both the PHP system and 1C is not huge. We haven’t faced performance issues.
Of course, I’ve read that large 1C implementations encounter challenges, but I don’t know how they resolve them. For example, we issue about 10,000 invoices a month.
Thank you very much!
Alright, folks, are there any more questions?
Pyotr, now a question for you: remember all the questions that were asked and choose one that’s the most interesting or tricky.
The trickiest question is why everyone pulls the blanket over themselves. Is it necessary to write code in both systems?
In my opinion, we should limit ourselves to one side and focus. Convince me otherwise!
Thank you very much!
Please raise your hands again to receive a gift. On behalf of the organizers, thank you for your preparation and interesting talk. It was great!
Thank you! I appreciate the interesting questions. As a host, it’s always a pleasure to hear questions, and it feels like people were genuinely listening rather than just sitting in work chats or on Instagram.
With that, I officially conclude today’s events in this hall. This was Ekaterina Firsova, HR Director at Altenar. It was a pleasure to work with such a responsive audience.
As a small bonus, I’d like to promote two of my activities tomorrow. At 10:00, there will be a panel discussion with Grigory Bogdanov and Ilyas Salikhov, and a resume review session.
This is an opportunity to send in your resume, even if you’re not looking for a job, and hear feedback on how it looks from the outside. No contact details are needed—no emails or phone numbers, and no company names.
You can find the address to send your resume on the talk page or on the website, or in the chat by searching for the keyword "resume."
Also, tomorrow, we will have a presentation on what’s happening in the job market, with salary overviews. The presentation will be streamed but not recorded, featuring about seven or eight overviews from consulting agencies of varying degrees of confidentiality.
There’s both open and less open information, which might be interesting to many.
With that, I bid you farewell and remind you not to rush off. We all have an after-party waiting for us with snacks and drinks, so there will be plenty to talk about.
Have a great evening! See you tomorrow! Goodbye!