the catching of the database statement error would be in the server-side code, where you would detect if the error number is for a duplicate index error, and setup a message telling the user what was wrong with the data that they submitted, which in this case would be that the sku already exists.
here's the whole story - you always need error handling for statements that can fail. for database statements that can fail - connection, query, prepare, and execute, the simplest way of adding error handling, without adding logic at each statement, is to use exceptions for errors and in most cases simply let php catch and handle the exception, where php will use its error related settings to control what happens with the actual error information (database statement errors will 'automatically' get displayed/logged the same as php errors.) the exception to this rule is when inserting/updating duplicate or out of range user submitted data values, which are the only kind of database errors that the 'user' on your site can recover from (all other database errors are either due to programming mistakes, the database server not running, or nefarious activity, that the user doesn't need to know anything at all about), by potentially entering new value(s) and resubmitting the data. in these cases, your code would catch the exception, test if the error number is for something that your code is responsible for handling, and setup a message for the user letting them know what was wrong with the data that they submitted. for all other error numbers, just re-throw the exception and let php handle it. you would set the PDO error mode to exceptions when you make the connection.
the net result of submitting the form data to the server-side code for this Create/insert operation should be either a success value/message or failure message(s) - consisting of all the user/validation errors. since you are making an ajax request, you should create an array with a 'success' or 'error' key, with the appropriate values, json_encode() this and output it to the browser for the ajax success code to test and use.
some points about the posted code -
OOP is not about wrapping a class definitions around parts of your main code and adding $var-> in front of everything to get it to work. all this is doing is exchanging one defining/calling syntax for another one, taking 2-3 times the amount of typing to accomplish a task.
you won't be using a SELECT query anymore for this current operation, but when you do use a select query, list out the columns you are selecting. for cases where you just need a count of the number of matching rows, use a SELECT COUNT(*) ... query, then fetch and use the count value. related to this, the rowCount() method may not work for a data retrieval query for all database types, and should be avoided for these type of queries. you should always fetch all the data that a query returns (the current code should be producing an out of sync error since you didn't fetch the row(s) of data from the select query.) also, for a select query, the only variable name that must be unique is the final variable holding the data fetched from the query. just use simple names like $sql, $stmt, ... for the variables in the common code.
writing out variables/properties by themselves does nothing (doesn't even produce any php byte code.) why do you have those 9 lines in there?
if you use simple positional ? place-holders and implicit binding, by supplying an array to the ->execute([...]) call, it will save a ton of repetitive typing and typo mistakes.
any php error related settings should be in the php.ini on your system so that they can be changed at a single point. having a display_errors setting inside some conditional logic in your code makes no sense.